CImg.h

00001 /*
00002  #
00003  #  File            : CImg.h
00004  #                    ( C++ header file )
00005  #
00006  #  Description     : The C++ Template Image Processing Toolkit.
00007  #                    This file is the main component of the CImg Library project.
00008  #                    ( http://cimg.sourceforge.net )
00009  #
00010  #  Project manager : David Tschumperle.
00011  #                    ( http://www.greyc.ensicaen.fr/~dtschump/ )
00012  #
00013  #                    The complete list of contributors is available in file 'README.txt'
00014  #                    distributed within the CImg package.
00015  #
00016  #  Licenses        : This file is 'dual-licensed', you have to choose one
00017  #                    of the two licenses below to apply.
00018  #
00019  #                    CeCILL-C
00020  #                    The CeCILL-C license is close to the GNU LGPL.
00021  #                    ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
00022  #
00023  #                or  CeCILL v2.0
00024  #                    The CeCILL license is compatible with the GNU GPL.
00025  #                    ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
00026  #
00027  #  This software is governed either by the CeCILL or the CeCILL-C license
00028  #  under French law and abiding by the rules of distribution of free software.
00029  #  You can  use, modify and or redistribute the software under the terms of
00030  #  the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA
00031  #  at the following URL : "http://www.cecill.info".
00032  #
00033  #  As a counterpart to the access to the source code and  rights to copy,
00034  #  modify and redistribute granted by the license, users are provided only
00035  #  with a limited warranty  and the software's author,  the holder of the
00036  #  economic rights,  and the successive licensors  have only  limited
00037  #  liability.
00038  #
00039  #  In this respect, the user's attention is drawn to the risks associated
00040  #  with loading,  using,  modifying and/or developing or reproducing the
00041  #  software by the user in light of its specific status of free software,
00042  #  that may mean  that it is complicated to manipulate,  and  that  also
00043  #  therefore means  that it is reserved for developers  and  experienced
00044  #  professionals having in-depth computer knowledge. Users are therefore
00045  #  encouraged to load and test the software's suitability as regards their
00046  #  requirements in conditions enabling the security of their systems and/or
00047  #  data to be ensured and,  more generally, to use and operate it in the
00048  #  same conditions as regards security.
00049  #
00050  #  The fact that you are presently reading this means that you have had
00051  #  knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms.
00052  #
00053 */
00054 
00055 // Define version number of the library file.
00056 #ifndef cimg_version
00057 #define cimg_version 134
00058 
00059 /*-----------------------------------------------------------
00060  #
00061  # Test and auto-set CImg configuration variables
00062  # and include required headers.
00063  #
00064  # If you find that default configuration variables are
00065  # not adapted to your case, you can override their values
00066  # before including the header file "CImg.h"
00067  # (use the #define directive).
00068  #
00069  ------------------------------------------------------------*/
00070 
00071 // Include required standard C++ headers.
00072 #include <cstdio>
00073 #include <cstdlib>
00074 #include <cstdarg>
00075 #include <cstring>
00076 #include <cmath>
00077 #include <ctime>
00078 #include <exception>
00079 
00080 // Operating system configuration.
00081 //
00082 // Define 'cimg_OS' to : '0' for an unknown OS (will try to minize library dependancies).
00083 //                       '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
00084 //                       '2' for Microsoft Windows.
00085 //                       (autodetection is done by default).
00086 #ifndef cimg_OS
00087 #if defined(unix)        || defined(__unix)      || defined(__unix__) \
00088  || defined(linux)       || defined(__linux)     || defined(__linux__) \
00089  || defined(sun)         || defined(__sun) \
00090  || defined(BSD)         || defined(__OpenBSD__) || defined(__NetBSD__) \
00091  || defined(__FreeBSD__) || defined __DragonFly__ \
00092  || defined(sgi)         || defined(__sgi) \
00093  || defined(__MACOSX__)  || defined(__APPLE__) \
00094  || defined(__CYGWIN__)
00095 #define cimg_OS 1
00096 #elif defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) \
00097    || defined(WIN64)    || defined(_WIN64) || defined(__WIN64__)
00098 #define cimg_OS 2
00099 #else
00100 #define cimg_OS 0
00101 #endif
00102 #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
00103 #error CImg Library : Configuration variable 'cimg_OS' is badly defined.
00104 #error (valid values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
00105 #endif
00106 
00107 // Disable silly warnings on Microsoft VC++ compilers.
00108 #ifdef _MSC_VER
00109 #pragma warning(push)
00110 #pragma warning(disable:4311)
00111 #pragma warning(disable:4312)
00112 #pragma warning(disable:4800)
00113 #pragma warning(disable:4804)
00114 #pragma warning(disable:4996)
00115 #define _CRT_SECURE_NO_DEPRECATE 1
00116 #define _CRT_NONSTDC_NO_DEPRECATE 1
00117 #endif
00118 
00119 // Include OS-specific headers for system management.
00120 #if cimg_OS==1
00121 #include <sys/time.h>
00122 #include <unistd.h>
00123 #elif cimg_OS==2
00124 #include <windows.h>
00125 #ifndef _WIN32_IE
00126 #define _WIN32_IE 0x0400
00127 #endif
00128 #include <shlobj.h>
00129 #endif
00130 
00131 // Filename separator configuration.
00132 //
00133 // Default separator is '/' for Unix-based OS, and '\' or Windows.
00134 #ifndef cimg_file_separator
00135 #if cimg_OS==2
00136 #define cimg_file_separator '\\'
00137 #else
00138 #define cimg_file_separator '/'
00139 #endif
00140 #endif
00141 
00142 // Output messages verbosity configuration.
00143 //
00144 // Define 'cimg_verbosity' to : '0' to hide library messages (quiet mode).
00145 //                              '1' to print library messages on the console.
00146 //                              '2' to display library messages on a dialog window (default behavior).
00147 //                              '3' to do as '1' + add extra warnings (may slow down the code !).
00148 //                              '4' to do as '2' + add extra warnings (may slow down the code !).
00149 //
00150 // Define 'cimg_strict_warnings' to replace warning messages by exception throwns.
00151 //
00152 // Define 'cimg_use_vt100' to allow output of color messages (require VT100-compatible terminal).
00153 #ifndef cimg_verbosity
00154 #define cimg_verbosity 2
00155 #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4)
00156 #error CImg Library : Configuration variable 'cimg_verbosity' is badly defined.
00157 #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }).
00158 #endif
00159 
00160 // Display framework configuration.
00161 //
00162 // Define 'cimg_display' to : '0' to disable display capabilities.
00163 //                            '1' to use X-Window framework (X11).
00164 //                            '2' to use Microsoft GDI32 framework.
00165 #ifndef cimg_display
00166 #if cimg_OS==0
00167 #define cimg_display 0
00168 #elif cimg_OS==1
00169 #if defined(__MACOSX__) || defined(__APPLE__)
00170 #define cimg_display 1
00171 #else
00172 #define cimg_display 1
00173 #endif
00174 #elif cimg_OS==2
00175 #define cimg_display 2
00176 #endif
00177 #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
00178 #error CImg Library : Configuration variable 'cimg_display' is badly defined.
00179 #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
00180 #endif
00181 
00182 // Include display-specific headers.
00183 #if cimg_display==1
00184 #include <X11/Xlib.h>
00185 #include <X11/Xutil.h>
00186 #include <X11/keysym.h>
00187 #include <pthread.h>
00188 #ifdef cimg_use_xshm
00189 #include <sys/ipc.h>
00190 #include <sys/shm.h>
00191 #include <X11/extensions/XShm.h>
00192 #endif
00193 #ifdef cimg_use_xrandr
00194 #include <X11/extensions/Xrandr.h>
00195 #endif
00196 #endif
00197 
00198 // OpenMP configuration.
00199 // (http://www.openmp.org)
00200 //
00201 // Define 'cimg_use_openmp' to enable OpenMP support.
00202 //
00203 // OpenMP directives can be used in few CImg functions to get
00204 // advantages of multi-core CPUs. Using OpenMP is not mandatory.
00205 #ifdef cimg_use_openmp
00206 #include <omp.h>
00207 #endif
00208 
00209 // LibPNG configuration.
00210 // (http://www.libpng.org)
00211 //
00212 // Define 'cimg_use_png' to enable LibPNG support.
00213 //
00214 // LibPNG can be used in functions 'CImg<T>::{load,save}_png()'
00215 // to get a builtin support of PNG files. Using LibPNG is not mandatory.
00216 #ifdef cimg_use_png
00217 extern "C" {
00218 #include <png.h>
00219 }
00220 #endif
00221 
00222 // LibJPEG configuration.
00223 // (http://en.wikipedia.org/wiki/Libjpeg)
00224 //
00225 // Define 'cimg_use_jpeg' to enable LibJPEG support.
00226 //
00227 // LibJPEG can be used in functions 'CImg<T>::{load,save}_jpeg()'
00228 // to get a builtin support of JPEG files. Using LibJPEG is not mandatory.
00229 #ifdef cimg_use_jpeg
00230 extern "C" {
00231 #include <jpeglib.h>
00232 }
00233 #endif
00234 
00235 // LibTIFF configuration.
00236 // (http://www.libtiff.org)
00237 //
00238 // Define 'cimg_use_tiff' to enable LibTIFF support.
00239 //
00240 // LibTIFF can be used in functions 'CImg[List]<T>::{load,save}_tiff()'
00241 // to get a builtin support of TIFF files. Using LibTIFF is not mandatory.
00242 #ifdef cimg_use_tiff
00243 extern "C" {
00244 #include <tiffio.h>
00245 }
00246 #endif
00247 
00248 // FFMPEG Avcodec and Avformat libraries configuration.
00249 // (http://www.ffmpeg.org)
00250 //
00251 // Define 'cimg_use_ffmpeg' to enable FFMPEG lib support.
00252 //
00253 // Avcodec and Avformat libraries can be used in functions
00254 // 'CImg[List]<T>::load_ffmpeg()' to get a builtin
00255 // support of various image sequences files.
00256 // Using FFMPEG libraries is not mandatory.
00257 #ifdef cimg_use_ffmpeg
00258 extern "C" {
00259 #include <avformat.h>
00260 #include <avcodec.h>
00261 #include <swscale.h>
00262 }
00263 #endif
00264 
00265 // Zlib configuration
00266 // (http://www.zlib.net)
00267 //
00268 // Define 'cimg_use_zlib' to enable Zlib support.
00269 //
00270 // Zlib can be used in functions 'CImg[List]<T>::{load,save}_cimg()'
00271 // to allow compressed data in '.cimg' files. Using Zlib is not mandatory.
00272 #ifdef cimg_use_zlib
00273 extern "C" {
00274 #include <zlib.h>
00275 }
00276 #endif
00277 
00278 // Magick++ configuration.
00279 // (http://www.imagemagick.org/Magick++)
00280 //
00281 // Define 'cimg_use_magick' to enable Magick++ support.
00282 //
00283 // Magick++ library can be used in functions 'CImg<T>::{load,save}()'
00284 // to get a builtin support of various image formats (PNG,JPEG,TIFF,...).
00285 // Using Magick++ is not mandatory.
00286 #ifdef cimg_use_magick
00287 #include <Magick++.h>
00288 #endif
00289 
00290 // FFTW3 configuration.
00291 // (http://www.fftw.org)
00292 //
00293 // Define 'cimg_use_fftw3' to enable libFFTW3 support.
00294 //
00295 // FFTW3 library can be used in functions 'CImg[List]<T>::FFT()' to
00296 // efficiently compute the Fast Fourier Transform of image data.
00297 #ifdef cimg_use_fftw3
00298 extern "C" {
00299 #include <fftw3.h>
00300 }
00301 #endif
00302 
00303 // Board configuration
00304 // (http://libboard.sourceforge.net/)
00305 //
00306 // Define 'cimg_use_board' to enable Board support.
00307 //
00308 // Board library can be used in functions 'CImg<T>::draw_object3d()'
00309 // to draw objects 3d in vector-graphics canvas that can be saved
00310 // as .PS or .SVG files afterwards.
00311 #ifdef cimg_use_board
00312 #ifdef None
00313 #undef None
00314 #define _cimg_redefine_None
00315 #endif
00316 #include <Board.h>
00317 #endif
00318 
00319 // OpenEXR configuration
00320 // (http://www.openexr.com/)
00321 //
00322 // Define 'cimg_use_openexr' to enable OpenEXR support.
00323 //
00324 // OpenEXR can be used to read/write .exr file formats.
00325 #ifdef cimg_use_openexr
00326 #include "ImfRgbaFile.h"
00327 #include "ImfInputFile.h"
00328 #include "ImfChannelList.h"
00329 #include "ImfMatrixAttribute.h"
00330 #include "ImfArray.h"
00331 #endif
00332 
00333 // Lapack configuration.
00334 // (http://www.netlib.org/lapack)
00335 //
00336 // Define 'cimg_use_lapack' to enable LAPACK support.
00337 //
00338 // Lapack can be used in various CImg functions dealing with
00339 // matrix computation and algorithms (eigenvalues, inverse, ...).
00340 // Using Lapack is not mandatory.
00341 #ifdef cimg_use_lapack
00342 extern "C" {
00343   extern void sgetrf_(int*, int*, float*, int*, int*, int*);
00344   extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
00345   extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
00346   extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
00347   extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
00348   extern void dgetrf_(int*, int*, double*, int*, int*, int*);
00349   extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
00350   extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
00351   extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, int*, double*, int*, double*, int*, int*);
00352   extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
00353 }
00354 #endif
00355 
00356 // Check if min/max/PI macros are defined.
00357 //
00358 // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
00359 // because min(), max() and PI labels are defined and used in the cimg:: namespace.
00360 // so it '#undef' these macros if necessary, and restore them to reasonable
00361 // values at the end of the file.
00362 #ifdef min
00363 #undef min
00364 #define _cimg_redefine_min
00365 #endif
00366 #ifdef max
00367 #undef max
00368 #define _cimg_redefine_max
00369 #endif
00370 #ifdef PI
00371 #undef PI
00372 #define _cimg_redefine_PI
00373 #endif
00374 
00375 /*------------------------------------------------------------------------------
00376   #
00377   # Define user-friendly macros.
00378   #
00379   # User macros are prefixed by 'cimg_' and can be used in your own code.
00380   # They are particularly useful for option parsing, and image loops creation.
00381   #
00382   ------------------------------------------------------------------------------*/
00383 
00384 // Define the program usage, and retrieve command line arguments.
00385 #define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
00386 #define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0)
00387 #define cimg_option(name,defaut,usage) cimg_library::cimg::option(name,argc,argv,defaut,usage)
00388 #define cimg_argument(pos) cimg_library::cimg::argument(pos,argc,argv)
00389 #define cimg_argument1(pos,s0) cimg_library::cimg::argument(pos,argc,argv,1,s0)
00390 #define cimg_argument2(pos,s0,s1) cimg_library::cimg::argument(pos,argc,argv,2,s0,s1)
00391 #define cimg_argument3(pos,s0,s1,s2) cimg_library::cimg::argument(pos,argc,argv,3,s0,s1,s2)
00392 #define cimg_argument4(pos,s0,s1,s2,s3) cimg_library::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3)
00393 #define cimg_argument5(pos,s0,s1,s2,s3,s4) cimg_library::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4)
00394 #define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) cimg_library::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5)
00395 #define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) cimg_library::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6)
00396 #define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) cimg_library::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7)
00397 #define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) cimg_library::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8)
00398 
00399 // Define and manipulate local neighborhoods.
00400 #define CImg_2x2(I,T) T I[4]; \
00401                       T& I##cc = I[0]; T& I##nc = I[1]; \
00402                       T& I##cn = I[2]; T& I##nn = I[3]; \
00403                       I##cc = I##nc = \
00404                       I##cn = I##nn = 0
00405 
00406 #define CImg_3x3(I,T) T I[9]; \
00407                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
00408                       T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
00409                       T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
00410                       I##pp = I##cp = I##np = \
00411                       I##pc = I##cc = I##nc = \
00412                       I##pn = I##cn = I##nn = 0
00413 
00414 #define CImg_4x4(I,T) T I[16]; \
00415                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
00416                       T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
00417                       T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
00418                       T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
00419                       I##pp = I##cp = I##np = I##ap = \
00420                       I##pc = I##cc = I##nc = I##ac = \
00421                       I##pn = I##cn = I##nn = I##an = \
00422                       I##pa = I##ca = I##na = I##aa = 0
00423 
00424 #define CImg_5x5(I,T) T I[25]; \
00425                       T& I##bb = I[0]; T& I##pb = I[1]; T& I##cb = I[2]; T& I##nb = I[3]; T& I##ab = I[4]; \
00426                       T& I##bp = I[5]; T& I##pp = I[6]; T& I##cp = I[7]; T& I##np = I[8]; T& I##ap = I[9]; \
00427                       T& I##bc = I[10]; T& I##pc = I[11]; T& I##cc = I[12]; T& I##nc = I[13]; T& I##ac = I[14]; \
00428                       T& I##bn = I[15]; T& I##pn = I[16]; T& I##cn = I[17]; T& I##nn = I[18]; T& I##an = I[19]; \
00429                       T& I##ba = I[20]; T& I##pa = I[21]; T& I##ca = I[22]; T& I##na = I[23]; T& I##aa = I[24]; \
00430                       I##bb = I##pb = I##cb = I##nb = I##ab = \
00431                       I##bp = I##pp = I##cp = I##np = I##ap = \
00432                       I##bc = I##pc = I##cc = I##nc = I##ac = \
00433                       I##bn = I##pn = I##cn = I##nn = I##an = \
00434                       I##ba = I##pa = I##ca = I##na = I##aa = 0
00435 
00436 #define CImg_2x2x2(I,T) T I[8]; \
00437                       T& I##ccc = I[0]; T& I##ncc = I[1]; \
00438                       T& I##cnc = I[2]; T& I##nnc = I[3]; \
00439                       T& I##ccn = I[4]; T& I##ncn = I[5]; \
00440                       T& I##cnn = I[6]; T& I##nnn = I[7]; \
00441                       I##ccc = I##ncc = \
00442                       I##cnc = I##nnc = \
00443                       I##ccn = I##ncn = \
00444                       I##cnn = I##nnn = 0
00445 
00446 #define CImg_3x3x3(I,T) T I[27]; \
00447                       T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
00448                       T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
00449                       T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
00450                       T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
00451                       T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
00452                       T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
00453                       T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
00454                       T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
00455                       T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
00456                       I##ppp = I##cpp = I##npp = \
00457                       I##pcp = I##ccp = I##ncp = \
00458                       I##pnp = I##cnp = I##nnp = \
00459                       I##ppc = I##cpc = I##npc = \
00460                       I##pcc = I##ccc = I##ncc = \
00461                       I##pnc = I##cnc = I##nnc = \
00462                       I##ppn = I##cpn = I##npn = \
00463                       I##pcn = I##ccn = I##ncn = \
00464                       I##pnn = I##cnn = I##nnn = 0
00465 
00466 #define cimg_get2x2(img,x,y,z,c,I,T) \
00467   I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), I[3] = (T)(img)(_n1##x,_n1##y,z,c)
00468 
00469 #define cimg_get3x3(img,x,y,z,c,I,T) \
00470   I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), I[3] = (T)(img)(_p1##x,y,z,c), \
00471   I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), \
00472   I[8] = (T)(img)(_n1##x,_n1##y,z,c)
00473 
00474 #define cimg_get4x4(img,x,y,z,c,I,T) \
00475   I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), I[3] = (T)(img)(_n2##x,_p1##y,z,c), \
00476   I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), \
00477   I[8] = (T)(img)(_p1##x,_n1##y,z,c), I[9] = (T)(img)(x,_n1##y,z,c), I[10] = (T)(img)(_n1##x,_n1##y,z,c), I[11] = (T)(img)(_n2##x,_n1##y,z,c), \
00478   I[12] = (T)(img)(_p1##x,_n2##y,z,c), I[13] = (T)(img)(x,_n2##y,z,c), I[14] = (T)(img)(_n1##x,_n2##y,z,c), I[15] = (T)(img)(_n2##x,_n2##y,z,c)
00479 
00480 #define cimg_get5x5(img,x,y,z,c,I,T) \
00481   I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), I[3] = (T)(img)(_n1##x,_p2##y,z,c), \
00482   I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), \
00483   I[8] = (T)(img)(_n1##x,_p1##y,z,c), I[9] = (T)(img)(_n2##x,_p1##y,z,c), I[10] = (T)(img)(_p2##x,y,z,c), I[11] = (T)(img)(_p1##x,y,z,c), \
00484   I[12] = (T)(img)(x,y,z,c), I[13] = (T)(img)(_n1##x,y,z,c), I[14] = (T)(img)(_n2##x,y,z,c), I[15] = (T)(img)(_p2##x,_n1##y,z,c), \
00485   I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), \
00486   I[20] = (T)(img)(_p2##x,_n2##y,z,c), I[21] = (T)(img)(_p1##x,_n2##y,z,c), I[22] = (T)(img)(x,_n2##y,z,c), I[23] = (T)(img)(_n1##x,_n2##y,z,c), \
00487   I[24] = (T)(img)(_n2##x,_n2##y,z,c)
00488 
00489 #define cimg_get6x6(img,x,y,z,c,I,T) \
00490  I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), I[3] = (T)(img)(_n1##x,_p2##y,z,c), \
00491  I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), \
00492  I[8] = (T)(img)(x,_p1##y,z,c), I[9] = (T)(img)(_n1##x,_p1##y,z,c), I[10] = (T)(img)(_n2##x,_p1##y,z,c), I[11] = (T)(img)(_n3##x,_p1##y,z,c), \
00493  I[12] = (T)(img)(_p2##x,y,z,c), I[13] = (T)(img)(_p1##x,y,z,c), I[14] = (T)(img)(x,y,z,c), I[15] = (T)(img)(_n1##x,y,z,c), \
00494  I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), \
00495  I[20] = (T)(img)(x,_n1##y,z,c), I[21] = (T)(img)(_n1##x,_n1##y,z,c), I[22] = (T)(img)(_n2##x,_n1##y,z,c), I[23] = (T)(img)(_n3##x,_n1##y,z,c), \
00496  I[24] = (T)(img)(_p2##x,_n2##y,z,c), I[25] = (T)(img)(_p1##x,_n2##y,z,c), I[26] = (T)(img)(x,_n2##y,z,c), I[27] = (T)(img)(_n1##x,_n2##y,z,c), \
00497  I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), \
00498  I[32] = (T)(img)(x,_n3##y,z,c), I[33] = (T)(img)(_n1##x,_n3##y,z,c), I[34] = (T)(img)(_n2##x,_n3##y,z,c), I[35] = (T)(img)(_n3##x,_n3##y,z,c)
00499 
00500 #define cimg_get7x7(img,x,y,z,c,I,T) \
00501  I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), I[3] = (T)(img)(x,_p3##y,z,c), \
00502  I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), \
00503  I[8] = (T)(img)(_p2##x,_p2##y,z,c), I[9] = (T)(img)(_p1##x,_p2##y,z,c), I[10] = (T)(img)(x,_p2##y,z,c), I[11] = (T)(img)(_n1##x,_p2##y,z,c), \
00504  I[12] = (T)(img)(_n2##x,_p2##y,z,c), I[13] = (T)(img)(_n3##x,_p2##y,z,c), I[14] = (T)(img)(_p3##x,_p1##y,z,c), I[15] = (T)(img)(_p2##x,_p1##y,z,c), \
00505  I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), \
00506  I[20] = (T)(img)(_n3##x,_p1##y,z,c), I[21] = (T)(img)(_p3##x,y,z,c), I[22] = (T)(img)(_p2##x,y,z,c), I[23] = (T)(img)(_p1##x,y,z,c), \
00507  I[24] = (T)(img)(x,y,z,c), I[25] = (T)(img)(_n1##x,y,z,c), I[26] = (T)(img)(_n2##x,y,z,c), I[27] = (T)(img)(_n3##x,y,z,c), \
00508  I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), \
00509  I[32] = (T)(img)(_n1##x,_n1##y,z,c), I[33] = (T)(img)(_n2##x,_n1##y,z,c), I[34] = (T)(img)(_n3##x,_n1##y,z,c), I[35] = (T)(img)(_p3##x,_n2##y,z,c), \
00510  I[36] = (T)(img)(_p2##x,_n2##y,z,c), I[37] = (T)(img)(_p1##x,_n2##y,z,c), I[38] = (T)(img)(x,_n2##y,z,c), I[39] = (T)(img)(_n1##x,_n2##y,z,c), \
00511  I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), \
00512  I[44] = (T)(img)(_p1##x,_n3##y,z,c), I[45] = (T)(img)(x,_n3##y,z,c), I[46] = (T)(img)(_n1##x,_n3##y,z,c), I[47] = (T)(img)(_n2##x,_n3##y,z,c), \
00513  I[48] = (T)(img)(_n3##x,_n3##y,z,c)
00514 
00515 #define cimg_get8x8(img,x,y,z,c,I,T) \
00516  I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), I[3] = (T)(img)(x,_p3##y,z,c), \
00517  I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), \
00518  I[8] = (T)(img)(_p3##x,_p2##y,z,c), I[9] = (T)(img)(_p2##x,_p2##y,z,c), I[10] = (T)(img)(_p1##x,_p2##y,z,c), I[11] = (T)(img)(x,_p2##y,z,c), \
00519  I[12] = (T)(img)(_n1##x,_p2##y,z,c), I[13] = (T)(img)(_n2##x,_p2##y,z,c), I[14] = (T)(img)(_n3##x,_p2##y,z,c), I[15] = (T)(img)(_n4##x,_p2##y,z,c), \
00520  I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), \
00521  I[20] = (T)(img)(_n1##x,_p1##y,z,c), I[21] = (T)(img)(_n2##x,_p1##y,z,c), I[22] = (T)(img)(_n3##x,_p1##y,z,c), I[23] = (T)(img)(_n4##x,_p1##y,z,c), \
00522  I[24] = (T)(img)(_p3##x,y,z,c), I[25] = (T)(img)(_p2##x,y,z,c), I[26] = (T)(img)(_p1##x,y,z,c), I[27] = (T)(img)(x,y,z,c), \
00523  I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), \
00524  I[32] = (T)(img)(_p3##x,_n1##y,z,c), I[33] = (T)(img)(_p2##x,_n1##y,z,c), I[34] = (T)(img)(_p1##x,_n1##y,z,c), I[35] = (T)(img)(x,_n1##y,z,c), \
00525  I[36] = (T)(img)(_n1##x,_n1##y,z,c), I[37] = (T)(img)(_n2##x,_n1##y,z,c), I[38] = (T)(img)(_n3##x,_n1##y,z,c), I[39] = (T)(img)(_n4##x,_n1##y,z,c), \
00526  I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), \
00527  I[44] = (T)(img)(_n1##x,_n2##y,z,c), I[45] = (T)(img)(_n2##x,_n2##y,z,c), I[46] = (T)(img)(_n3##x,_n2##y,z,c), I[47] = (T)(img)(_n4##x,_n2##y,z,c), \
00528  I[48] = (T)(img)(_p3##x,_n3##y,z,c), I[49] = (T)(img)(_p2##x,_n3##y,z,c), I[50] = (T)(img)(_p1##x,_n3##y,z,c), I[51] = (T)(img)(x,_n3##y,z,c), \
00529  I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), \
00530  I[56] = (T)(img)(_p3##x,_n4##y,z,c), I[57] = (T)(img)(_p2##x,_n4##y,z,c), I[58] = (T)(img)(_p1##x,_n4##y,z,c), I[59] = (T)(img)(x,_n4##y,z,c), \
00531  I[60] = (T)(img)(_n1##x,_n4##y,z,c), I[61] = (T)(img)(_n2##x,_n4##y,z,c), I[62] = (T)(img)(_n3##x,_n4##y,z,c), I[63] = (T)(img)(_n4##x,_n4##y,z,c);
00532 
00533 #define cimg_get9x9(img,x,y,z,c,I,T) \
00534  I[0] = (T)(img)(_p4##x,_p4##y,z,c), I[1] = (T)(img)(_p3##x,_p4##y,z,c), I[2] = (T)(img)(_p2##x,_p4##y,z,c), I[3] = (T)(img)(_p1##x,_p4##y,z,c), \
00535  I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), \
00536  I[8] = (T)(img)(_n4##x,_p4##y,z,c), I[9] = (T)(img)(_p4##x,_p3##y,z,c), I[10] = (T)(img)(_p3##x,_p3##y,z,c), I[11] = (T)(img)(_p2##x,_p3##y,z,c), \
00537  I[12] = (T)(img)(_p1##x,_p3##y,z,c), I[13] = (T)(img)(x,_p3##y,z,c), I[14] = (T)(img)(_n1##x,_p3##y,z,c), I[15] = (T)(img)(_n2##x,_p3##y,z,c), \
00538  I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), \
00539  I[20] = (T)(img)(_p2##x,_p2##y,z,c), I[21] = (T)(img)(_p1##x,_p2##y,z,c), I[22] = (T)(img)(x,_p2##y,z,c), I[23] = (T)(img)(_n1##x,_p2##y,z,c), \
00540  I[24] = (T)(img)(_n2##x,_p2##y,z,c), I[25] = (T)(img)(_n3##x,_p2##y,z,c), I[26] = (T)(img)(_n4##x,_p2##y,z,c), I[27] = (T)(img)(_p4##x,_p1##y,z,c), \
00541  I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), \
00542  I[32] = (T)(img)(_n1##x,_p1##y,z,c), I[33] = (T)(img)(_n2##x,_p1##y,z,c), I[34] = (T)(img)(_n3##x,_p1##y,z,c), I[35] = (T)(img)(_n4##x,_p1##y,z,c), \
00543  I[36] = (T)(img)(_p4##x,y,z,c), I[37] = (T)(img)(_p3##x,y,z,c), I[38] = (T)(img)(_p2##x,y,z,c), I[39] = (T)(img)(_p1##x,y,z,c), \
00544  I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), \
00545  I[44] = (T)(img)(_n4##x,y,z,c), I[45] = (T)(img)(_p4##x,_n1##y,z,c), I[46] = (T)(img)(_p3##x,_n1##y,z,c), I[47] = (T)(img)(_p2##x,_n1##y,z,c), \
00546  I[48] = (T)(img)(_p1##x,_n1##y,z,c), I[49] = (T)(img)(x,_n1##y,z,c), I[50] = (T)(img)(_n1##x,_n1##y,z,c), I[51] = (T)(img)(_n2##x,_n1##y,z,c), \
00547  I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), \
00548  I[56] = (T)(img)(_p2##x,_n2##y,z,c), I[57] = (T)(img)(_p1##x,_n2##y,z,c), I[58] = (T)(img)(x,_n2##y,z,c), I[59] = (T)(img)(_n1##x,_n2##y,z,c), \
00549  I[60] = (T)(img)(_n2##x,_n2##y,z,c), I[61] = (T)(img)(_n3##x,_n2##y,z,c), I[62] = (T)(img)(_n4##x,_n2##y,z,c), I[63] = (T)(img)(_p4##x,_n3##y,z,c), \
00550  I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), \
00551  I[68] = (T)(img)(_n1##x,_n3##y,z,c), I[69] = (T)(img)(_n2##x,_n3##y,z,c), I[70] = (T)(img)(_n3##x,_n3##y,z,c), I[71] = (T)(img)(_n4##x,_n3##y,z,c), \
00552  I[72] = (T)(img)(_p4##x,_n4##y,z,c), I[73] = (T)(img)(_p3##x,_n4##y,z,c), I[74] = (T)(img)(_p2##x,_n4##y,z,c), I[75] = (T)(img)(_p1##x,_n4##y,z,c), \
00553  I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), \
00554  I[80] = (T)(img)(_n4##x,_n4##y,z,c)
00555 
00556 #define cimg_get2x2x2(img,x,y,z,c,I,T) \
00557   I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), I[3] = (T)(img)(_n1##x,_n1##y,z,c), \
00558   I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
00559 
00560 #define cimg_get3x3x3(img,x,y,z,c,I,T) \
00561   I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), \
00562   I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), I[5] = (T)(img)(_n1##x,y,_p1##z,c), \
00563   I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), \
00564   I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), I[11] = (T)(img)(_n1##x,_p1##y,z,c), \
00565   I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), I[14] = (T)(img)(_n1##x,y,z,c), \
00566   I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), I[17] = (T)(img)(_n1##x,_n1##y,z,c), \
00567   I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), \
00568   I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), I[23] = (T)(img)(_n1##x,y,_n1##z,c), \
00569   I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
00570 
00571 // Define various image loops.
00572 //
00573 // These macros generally avoid the use of iterators, but you are not forced to used them !
00574 #define cimg_for(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size(); (ptrs--)>(img)._data; )
00575 #define cimg_foroff(img,off) for (unsigned int off = 0, _max##off = (unsigned int)(img).size(); off<_max##off; ++off)
00576 
00577 #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
00578 #define cimg_forX(img,x) cimg_for1((img)._width,x)
00579 #define cimg_forY(img,y) cimg_for1((img)._height,y)
00580 #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
00581 #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
00582 #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
00583 #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
00584 #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
00585 #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
00586 #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
00587 #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
00588 #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
00589 #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
00590 #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
00591 #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
00592 #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
00593 
00594 #define cimg_for_in1(bound,i0,i1,i) \
00595  for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound)-1; i<=_max##i; ++i)
00596 #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
00597 #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
00598 #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
00599 #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
00600 #define cimg_for_inXY(img,x0,y0,x1,y1,x,y) cimg_for_inY(img,y0,y1,y) cimg_for_inX(img,x0,x1,x)
00601 #define cimg_for_inXZ(img,x0,z0,x1,z1,x,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inX(img,x0,x1,x)
00602 #define cimg_for_inXC(img,x0,c0,x1,c1,x,c) cimg_for_inC(img,c0,c1,c) cimg_for_inX(img,x0,x1,x)
00603 #define cimg_for_inYZ(img,y0,z0,y1,z1,y,z) cimg_for_inZ(img,x0,z1,z) cimg_for_inY(img,y0,y1,y)
00604 #define cimg_for_inYC(img,y0,c0,y1,c1,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inY(img,y0,y1,y)
00605 #define cimg_for_inZC(img,z0,c0,z1,c1,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inZ(img,z0,z1,z)
00606 #define cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inXY(img,x0,y0,x1,y1,x,y)
00607 #define cimg_for_inXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXY(img,x0,y0,x1,y1,x,y)
00608 #define cimg_for_inXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXZ(img,x0,z0,x1,z1,x,z)
00609 #define cimg_for_inYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inYZ(img,y0,z0,y1,z1,y,z)
00610 #define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
00611 #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width-1-(n),x)
00612 #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height-1-(n),y)
00613 #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth-1-(n),z)
00614 #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum-1-(n),c)
00615 #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
00616 #define cimg_for_insideXYZ(img,x,y,z,n) cimg_for_inXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
00617 #define cimg_for_insideXYZC(img,x,y,z,c,n) cimg_for_inXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
00618 
00619 #define cimg_for_out1(boundi,i0,i1,i) \
00620  for (int i = (int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1)+1:i)
00621 #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
00622  for (int j = 0; j<(int)(boundj); ++j) \
00623  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
00624   ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1)+1:i))
00625 #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
00626  for (int k = 0; k<(int)(boundk); ++k) \
00627  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
00628  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
00629   ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1)+1:i))
00630 #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
00631  for (int l = 0; l<(int)(boundl); ++l) \
00632  for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
00633  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
00634  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
00635   ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1)+1:i))
00636 #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
00637 #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
00638 #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
00639 #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
00640 #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
00641 #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
00642 #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
00643 #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
00644 #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
00645 #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
00646 #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z)
00647 #define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c)
00648 #define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c)
00649 #define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c)
00650 #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
00651  cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
00652 #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width-1-(n),x)
00653 #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height-1-(n),y)
00654 #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth-1-(n),z)
00655 #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum-1-(n),c)
00656 #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
00657 #define cimg_for_borderXYZ(img,x,y,z,n) cimg_for_outXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
00658 #define cimg_for_borderXYZC(img,x,y,z,c,n) \
00659  cimg_for_outXYZC(img,n,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),(img)._spectrum-1-(n),x,y,z,c)
00660 
00661 #define cimg_for_spiralXY(img,x,y) \
00662  for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
00663       --_n1##y, _n1##x+=(_n1##x>>2)-((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width-1-++x:((_n1##x&3)==2?(img)._height-1-++y:--x))))?0:1)
00664 
00665 #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
00666  for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
00667       _dx=(x1)>(x0)?(int)(x1)-(int)(x0):(_sx=-1,(int)(x0)-(int)(x1)), \
00668       _dy=(y1)>(y0)?(int)(y1)-(int)(y0):(_sy=-1,(int)(y0)-(int)(y1)), \
00669       _counter = _dx, \
00670       _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
00671       _counter>=0; \
00672       --_counter, x+=_steep? \
00673       (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
00674       (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
00675 
00676 #define cimg_for2(bound,i) \
00677  for (int i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1; \
00678       _n1##i<(int)(bound) || i==--_n1##i; \
00679       ++i, ++_n1##i)
00680 #define cimg_for2X(img,x) cimg_for2((img)._width,x)
00681 #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
00682 #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
00683 #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
00684 #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
00685 #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
00686 #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
00687 #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
00688 #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
00689 #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
00690 #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
00691 #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
00692 #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
00693 #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
00694 
00695 #define cimg_for_in2(bound,i0,i1,i) \
00696  for (int i = (int)(i0)<0?0:(int)(i0), \
00697       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
00698       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
00699       ++i, ++_n1##i)
00700 #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
00701 #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
00702 #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
00703 #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
00704 #define cimg_for_in2XY(img,x0,y0,x1,y1,x,y) cimg_for_in2Y(img,y0,y1,y) cimg_for_in2X(img,x0,x1,x)
00705 #define cimg_for_in2XZ(img,x0,z0,x1,z1,x,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2X(img,x0,x1,x)
00706 #define cimg_for_in2XC(img,x0,c0,x1,c1,x,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2X(img,x0,x1,x)
00707 #define cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2Y(img,y0,y1,y)
00708 #define cimg_for_in2YC(img,y0,c0,y1,c1,y,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Y(img,y0,y1,y)
00709 #define cimg_for_in2ZC(img,z0,c0,z1,c1,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Z(img,z0,z1,z)
00710 #define cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2XY(img,x0,y0,x1,y1,x,y)
00711 #define cimg_for_in2XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z)
00712 #define cimg_for_in2YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z)
00713 #define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
00714 
00715 #define cimg_for3(bound,i) \
00716  for (int i = 0, _p1##i = 0, \
00717       _n1##i = 1>=(bound)?(int)(bound)-1:1; \
00718       _n1##i<(int)(bound) || i==--_n1##i; \
00719       _p1##i = i++, ++_n1##i)
00720 #define cimg_for3X(img,x) cimg_for3((img)._width,x)
00721 #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
00722 #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
00723 #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
00724 #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
00725 #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
00726 #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
00727 #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
00728 #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
00729 #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
00730 #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
00731 #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
00732 #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
00733 #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
00734 
00735 #define cimg_for_in3(bound,i0,i1,i) \
00736  for (int i = (int)(i0)<0?0:(int)(i0), \
00737       _p1##i = i-1<0?0:i-1, \
00738       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
00739       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
00740       _p1##i = i++, ++_n1##i)
00741 #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
00742 #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
00743 #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
00744 #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
00745 #define cimg_for_in3XY(img,x0,y0,x1,y1,x,y) cimg_for_in3Y(img,y0,y1,y) cimg_for_in3X(img,x0,x1,x)
00746 #define cimg_for_in3XZ(img,x0,z0,x1,z1,x,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3X(img,x0,x1,x)
00747 #define cimg_for_in3XC(img,x0,c0,x1,c1,x,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3X(img,x0,x1,x)
00748 #define cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3Y(img,y0,y1,y)
00749 #define cimg_for_in3YC(img,y0,c0,y1,c1,y,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Y(img,y0,y1,y)
00750 #define cimg_for_in3ZC(img,z0,c0,z1,c1,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Z(img,z0,z1,z)
00751 #define cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3XY(img,x0,y0,x1,y1,x,y)
00752 #define cimg_for_in3XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z)
00753 #define cimg_for_in3YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z)
00754 #define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
00755 
00756 #define cimg_for4(bound,i) \
00757  for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1, \
00758       _n2##i = 2>=(bound)?(int)(bound)-1:2; \
00759       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
00760       _p1##i = i++, ++_n1##i, ++_n2##i)
00761 #define cimg_for4X(img,x) cimg_for4((img)._width,x)
00762 #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
00763 #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
00764 #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
00765 #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
00766 #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
00767 #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
00768 #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
00769 #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
00770 #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
00771 #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
00772 #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
00773 #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
00774 #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
00775 
00776 #define cimg_for_in4(bound,i0,i1,i) \
00777  for (int i = (int)(i0)<0?0:(int)(i0), \
00778       _p1##i = i-1<0?0:i-1, \
00779       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
00780       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
00781       i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
00782       _p1##i = i++, ++_n1##i, ++_n2##i)
00783 #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
00784 #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
00785 #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
00786 #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
00787 #define cimg_for_in4XY(img,x0,y0,x1,y1,x,y) cimg_for_in4Y(img,y0,y1,y) cimg_for_in4X(img,x0,x1,x)
00788 #define cimg_for_in4XZ(img,x0,z0,x1,z1,x,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4X(img,x0,x1,x)
00789 #define cimg_for_in4XC(img,x0,c0,x1,c1,x,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4X(img,x0,x1,x)
00790 #define cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4Y(img,y0,y1,y)
00791 #define cimg_for_in4YC(img,y0,c0,y1,c1,y,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Y(img,y0,y1,y)
00792 #define cimg_for_in4ZC(img,z0,c0,z1,c1,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Z(img,z0,z1,z)
00793 #define cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4XY(img,x0,y0,x1,y1,x,y)
00794 #define cimg_for_in4XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z)
00795 #define cimg_for_in4YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z)
00796 #define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
00797 
00798 #define cimg_for5(bound,i) \
00799  for (int i = 0, _p2##i = 0, _p1##i = 0, \
00800       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
00801       _n2##i = 2>=(bound)?(int)(bound)-1:2; \
00802       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
00803       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
00804 #define cimg_for5X(img,x) cimg_for5((img)._width,x)
00805 #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
00806 #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
00807 #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
00808 #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
00809 #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
00810 #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
00811 #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
00812 #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
00813 #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
00814 #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
00815 #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
00816 #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
00817 #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
00818 
00819 #define cimg_for_in5(bound,i0,i1,i) \
00820  for (int i = (int)(i0)<0?0:(int)(i0), \
00821       _p2##i = i-2<0?0:i-2, \
00822       _p1##i = i-1<0?0:i-1, \
00823       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
00824       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
00825       i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
00826       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
00827 #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
00828 #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
00829 #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
00830 #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
00831 #define cimg_for_in5XY(img,x0,y0,x1,y1,x,y) cimg_for_in5Y(img,y0,y1,y) cimg_for_in5X(img,x0,x1,x)
00832 #define cimg_for_in5XZ(img,x0,z0,x1,z1,x,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5X(img,x0,x1,x)
00833 #define cimg_for_in5XC(img,x0,c0,x1,c1,x,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5X(img,x0,x1,x)
00834 #define cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5Y(img,y0,y1,y)
00835 #define cimg_for_in5YC(img,y0,c0,y1,c1,y,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Y(img,y0,y1,y)
00836 #define cimg_for_in5ZC(img,z0,c0,z1,c1,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Z(img,z0,z1,z)
00837 #define cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5XY(img,x0,y0,x1,y1,x,y)
00838 #define cimg_for_in5XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z)
00839 #define cimg_for_in5YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z)
00840 #define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
00841 
00842 #define cimg_for6(bound,i) \
00843  for (int i = 0, _p2##i = 0, _p1##i = 0, \
00844       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
00845       _n2##i = 2>=(bound)?(int)(bound)-1:2, \
00846       _n3##i = 3>=(bound)?(int)(bound)-1:3; \
00847       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
00848       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
00849 #define cimg_for6X(img,x) cimg_for6((img)._width,x)
00850 #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
00851 #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
00852 #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
00853 #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
00854 #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
00855 #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
00856 #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
00857 #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
00858 #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
00859 #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
00860 #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
00861 #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
00862 #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
00863 
00864 #define cimg_for_in6(bound,i0,i1,i) \
00865  for (int i = (int)(i0)<0?0:(int)(i0), \
00866       _p2##i = i-2<0?0:i-2, \
00867       _p1##i = i-1<0?0:i-1, \
00868       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
00869       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
00870       _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
00871       i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
00872       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
00873 #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
00874 #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
00875 #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
00876 #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
00877 #define cimg_for_in6XY(img,x0,y0,x1,y1,x,y) cimg_for_in6Y(img,y0,y1,y) cimg_for_in6X(img,x0,x1,x)
00878 #define cimg_for_in6XZ(img,x0,z0,x1,z1,x,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6X(img,x0,x1,x)
00879 #define cimg_for_in6XC(img,x0,c0,x1,c1,x,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6X(img,x0,x1,x)
00880 #define cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6Y(img,y0,y1,y)
00881 #define cimg_for_in6YC(img,y0,c0,y1,c1,y,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Y(img,y0,y1,y)
00882 #define cimg_for_in6ZC(img,z0,c0,z1,c1,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Z(img,z0,z1,z)
00883 #define cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6XY(img,x0,y0,x1,y1,x,y)
00884 #define cimg_for_in6XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z)
00885 #define cimg_for_in6YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z)
00886 #define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
00887 
00888 #define cimg_for7(bound,i) \
00889  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
00890       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
00891       _n2##i = 2>=(bound)?(int)(bound)-1:2, \
00892       _n3##i = 3>=(bound)?(int)(bound)-1:3; \
00893       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
00894       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
00895 #define cimg_for7X(img,x) cimg_for7((img)._width,x)
00896 #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
00897 #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
00898 #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
00899 #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
00900 #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
00901 #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
00902 #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
00903 #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
00904 #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
00905 #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
00906 #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
00907 #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
00908 #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
00909 
00910 #define cimg_for_in7(bound,i0,i1,i) \
00911  for (int i = (int)(i0)<0?0:(int)(i0), \
00912       _p3##i = i-3<0?0:i-3, \
00913       _p2##i = i-2<0?0:i-2, \
00914       _p1##i = i-1<0?0:i-1, \
00915       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
00916       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
00917       _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
00918       i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
00919       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
00920 #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
00921 #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
00922 #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
00923 #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
00924 #define cimg_for_in7XY(img,x0,y0,x1,y1,x,y) cimg_for_in7Y(img,y0,y1,y) cimg_for_in7X(img,x0,x1,x)
00925 #define cimg_for_in7XZ(img,x0,z0,x1,z1,x,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7X(img,x0,x1,x)
00926 #define cimg_for_in7XC(img,x0,c0,x1,c1,x,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7X(img,x0,x1,x)
00927 #define cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7Y(img,y0,y1,y)
00928 #define cimg_for_in7YC(img,y0,c0,y1,c1,y,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Y(img,y0,y1,y)
00929 #define cimg_for_in7ZC(img,z0,c0,z1,c1,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Z(img,z0,z1,z)
00930 #define cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7XY(img,x0,y0,x1,y1,x,y)
00931 #define cimg_for_in7XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z)
00932 #define cimg_for_in7YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z)
00933 #define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
00934 
00935 #define cimg_for8(bound,i) \
00936  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
00937       _n1##i = 1>=(bound)?(int)(bound)-1:1, \
00938       _n2##i = 2>=(bound)?(int)(bound)-1:2, \
00939       _n3##i = 3>=(bound)?(int)(bound)-1:3, \
00940       _n4##i = 4>=(bound)?(int)(bound)-1:4; \
00941       _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
00942       i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
00943       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
00944 #define cimg_for8X(img,x) cimg_for8((img)._width,x)
00945 #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
00946 #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
00947 #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
00948 #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
00949 #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
00950 #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
00951 #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
00952 #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
00953 #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
00954 #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
00955 #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
00956 #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
00957 #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
00958 
00959 #define cimg_for_in8(bound,i0,i1,i) \
00960  for (int i = (int)(i0)<0?0:(int)(i0), \
00961       _p3##i = i-3<0?0:i-3, \
00962       _p2##i = i-2<0?0:i-2, \
00963       _p1##i = i-1<0?0:i-1, \
00964       _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
00965       _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
00966       _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
00967       _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
00968       i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
00969       i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
00970       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
00971 #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
00972 #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
00973 #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
00974 #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
00975 #define cimg_for_in8XY(img,x0,y0,x1,y1,x,y) cimg_for_in8Y(img,y0,y1,y) cimg_for_in8X(img,x0,x1,x)
00976 #define cimg_for_in8XZ(img,x0,z0,x1,z1,x,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8X(img,x0,x1,x)
00977 #define cimg_for_in8XC(img,x0,c0,x1,c1,x,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8X(img,x0,x1,x)
00978 #define cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8Y(img,y0,y1,y)
00979 #define cimg_for_in8YC(img,y0,c0,y1,c1,y,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Y(img,y0,y1,y)
00980 #define cimg_for_in8ZC(img,z0,c0,z1,c1,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Z(img,z0,z1,z)
00981 #define cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8XY(img,x0,y0,x1,y1,x,y)
00982 #define cimg_for_in8XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z)
00983 #define cimg_for_in8YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z)
00984 #define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
00985 
00986 #define cimg_for9(bound,i) \
00987   for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
00988        _n1##i = 1>=(int)(bound)?(int)(bound)-1:1, \
00989        _n2##i = 2>=(int)(bound)?(int)(bound)-1:2, \
00990        _n3##i = 3>=(int)(bound)?(int)(bound)-1:3, \
00991        _n4##i = 4>=(int)(bound)?(int)(bound)-1:4; \
00992        _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
00993        i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
00994        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
00995 #define cimg_for9X(img,x) cimg_for9((img)._width,x)
00996 #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
00997 #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
00998 #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
00999 #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
01000 #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
01001 #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
01002 #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
01003 #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
01004 #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
01005 #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
01006 #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
01007 #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
01008 #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
01009 
01010 #define cimg_for_in9(bound,i0,i1,i) \
01011   for (int i = (int)(i0)<0?0:(int)(i0), \
01012        _p4##i = i-4<0?0:i-4, \
01013        _p3##i = i-3<0?0:i-3, \
01014        _p2##i = i-2<0?0:i-2, \
01015        _p1##i = i-1<0?0:i-1, \
01016        _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
01017        _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
01018        _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
01019        _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
01020        i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
01021        i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
01022        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
01023 #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
01024 #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
01025 #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
01026 #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
01027 #define cimg_for_in9XY(img,x0,y0,x1,y1,x,y) cimg_for_in9Y(img,y0,y1,y) cimg_for_in9X(img,x0,x1,x)
01028 #define cimg_for_in9XZ(img,x0,z0,x1,z1,x,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9X(img,x0,x1,x)
01029 #define cimg_for_in9XC(img,x0,c0,x1,c1,x,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9X(img,x0,x1,x)
01030 #define cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9Y(img,y0,y1,y)
01031 #define cimg_for_in9YC(img,y0,c0,y1,c1,y,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Y(img,y0,y1,y)
01032 #define cimg_for_in9ZC(img,z0,c0,z1,c1,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Z(img,z0,z1,z)
01033 #define cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9XY(img,x0,y0,x1,y1,x,y)
01034 #define cimg_for_in9XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z)
01035 #define cimg_for_in9YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z)
01036 #define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
01037 
01038 #define cimg_for2x2(img,x,y,z,c,I,T) \
01039   cimg_for2((img)._height,y) for (int x = 0, \
01040    _n1##x = (int)( \
01041    (I[0] = (T)(img)(0,y,z,c)), \
01042    (I[2] = (T)(img)(0,_n1##y,z,c)), \
01043    1>=(img)._width?(img).width()-1:1);  \
01044    (_n1##x<(img).width() && ( \
01045    (I[1] = (T)(img)(_n1##x,y,z,c)), \
01046    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
01047    x==--_n1##x; \
01048    I[0] = I[1], \
01049    I[2] = I[3], \
01050    ++x, ++_n1##x)
01051 
01052 #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
01053   cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
01054    _n1##x = (int)( \
01055    (I[0] = (T)(img)(x,y,z,c)), \
01056    (I[2] = (T)(img)(x,_n1##y,z,c)), \
01057    x+1>=(int)(img)._width?(img).width()-1:x+1); \
01058    x<=(int)(x1) && ((_n1##x<(img).width() && (  \
01059    (I[1] = (T)(img)(_n1##x,y,z,c)), \
01060    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
01061    x==--_n1##x); \
01062    I[0] = I[1], \
01063    I[2] = I[3], \
01064    ++x, ++_n1##x)
01065 
01066 #define cimg_for3x3(img,x,y,z,c,I,T) \
01067   cimg_for3((img)._height,y) for (int x = 0, \
01068    _p1##x = 0, \
01069    _n1##x = (int)( \
01070    (I[0] = I[1] = (T)(img)(0,_p1##y,z,c)), \
01071    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
01072    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),      \
01073    1>=(img)._width?(img).width()-1:1); \
01074    (_n1##x<(img).width() && ( \
01075    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
01076    (I[5] = (T)(img)(_n1##x,y,z,c)), \
01077    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
01078    x==--_n1##x; \
01079    I[0] = I[1], I[1] = I[2], \
01080    I[3] = I[4], I[4] = I[5], \
01081    I[6] = I[7], I[7] = I[8], \
01082    _p1##x = x++, ++_n1##x)
01083 
01084 #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
01085   cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
01086    _p1##x = x-1<0?0:x-1, \
01087    _n1##x = (int)( \
01088    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
01089    (I[3] = (T)(img)(_p1##x,y,z,c)), \
01090    (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
01091    (I[1] = (T)(img)(x,_p1##y,z,c)), \
01092    (I[4] = (T)(img)(x,y,z,c)), \
01093    (I[7] = (T)(img)(x,_n1##y,z,c)), \
01094    x+1>=(int)(img)._width?(img).width()-1:x+1); \
01095    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
01096    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
01097    (I[5] = (T)(img)(_n1##x,y,z,c)), \
01098    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
01099    x==--_n1##x);            \
01100    I[0] = I[1], I[1] = I[2], \
01101    I[3] = I[4], I[4] = I[5], \
01102    I[6] = I[7], I[7] = I[8], \
01103    _p1##x = x++, ++_n1##x)
01104 
01105 #define cimg_for4x4(img,x,y,z,c,I,T) \
01106   cimg_for4((img)._height,y) for (int x = 0, \
01107    _p1##x = 0, \
01108    _n1##x = 1>=(img)._width?(img).width()-1:1, \
01109    _n2##x = (int)( \
01110    (I[0] = I[1] = (T)(img)(0,_p1##y,z,c)), \
01111    (I[4] = I[5] = (T)(img)(0,y,z,c)), \
01112    (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
01113    (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
01114    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
01115    (I[6] = (T)(img)(_n1##x,y,z,c)), \
01116    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
01117    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
01118    2>=(img)._width?(img).width()-1:2); \
01119    (_n2##x<(img).width() && ( \
01120    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
01121    (I[7] = (T)(img)(_n2##x,y,z,c)), \
01122    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
01123    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
01124    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
01125    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
01126    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
01127    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
01128    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
01129    _p1##x = x++, ++_n1##x, ++_n2##x)
01130 
01131 #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
01132   cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
01133    _p1##x = x-1<0?0:x-1, \
01134    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
01135    _n2##x = (int)( \
01136    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
01137    (I[4] = (T)(img)(_p1##x,y,z,c)), \
01138    (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
01139    (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
01140    (I[1] = (T)(img)(x,_p1##y,z,c)), \
01141    (I[5] = (T)(img)(x,y,z,c)), \
01142    (I[9] = (T)(img)(x,_n1##y,z,c)), \
01143    (I[13] = (T)(img)(x,_n2##y,z,c)), \
01144    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
01145    (I[6] = (T)(img)(_n1##x,y,z,c)), \
01146    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
01147    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
01148    x+2>=(int)(img)._width?(img).width()-1:x+2); \
01149    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
01150    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
01151    (I[7] = (T)(img)(_n2##x,y,z,c)), \
01152    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
01153    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
01154    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
01155    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
01156    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
01157    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
01158    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
01159    _p1##x = x++, ++_n1##x, ++_n2##x)
01160 
01161 #define cimg_for5x5(img,x,y,z,c,I,T) \
01162  cimg_for5((img)._height,y) for (int x = 0, \
01163    _p2##x = 0, _p1##x = 0, \
01164    _n1##x = 1>=(img)._width?(img).width()-1:1, \
01165    _n2##x = (int)( \
01166    (I[0] = I[1] = I[2] = (T)(img)(0,_p2##y,z,c)), \
01167    (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
01168    (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
01169    (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
01170    (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
01171    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
01172    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
01173    (I[13] = (T)(img)(_n1##x,y,z,c)), \
01174    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
01175    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)),  \
01176    2>=(img)._width?(img).width()-1:2); \
01177    (_n2##x<(img).width() && ( \
01178    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
01179    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
01180    (I[14] = (T)(img)(_n2##x,y,z,c)), \
01181    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
01182    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
01183    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
01184    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
01185    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
01186    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
01187    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
01188    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
01189    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
01190 
01191 #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
01192  cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
01193    _p2##x = x-2<0?0:x-2, \
01194    _p1##x = x-1<0?0:x-1, \
01195    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
01196    _n2##x = (int)( \
01197    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
01198    (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
01199    (I[10] = (T)(img)(_p2##x,y,z,c)), \
01200    (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
01201    (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
01202    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
01203    (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
01204    (I[11] = (T)(img)(_p1##x,y,z,c)), \
01205    (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
01206    (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
01207    (I[2] = (T)(img)(x,_p2##y,z,c)), \
01208    (I[7] = (T)(img)(x,_p1##y,z,c)), \
01209    (I[12] = (T)(img)(x,y,z,c)), \
01210    (I[17] = (T)(img)(x,_n1##y,z,c)), \
01211    (I[22] = (T)(img)(x,_n2##y,z,c)), \
01212    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
01213    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
01214    (I[13] = (T)(img)(_n1##x,y,z,c)), \
01215    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
01216    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
01217    x+2>=(int)(img)._width?(img).width()-1:x+2); \
01218    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
01219    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
01220    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
01221    (I[14] = (T)(img)(_n2##x,y,z,c)), \
01222    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
01223    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
01224    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
01225    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
01226    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
01227    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
01228    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
01229    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
01230    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
01231 
01232 #define cimg_for6x6(img,x,y,z,c,I,T) \
01233  cimg_for6((img)._height,y) for (int x = 0, \
01234    _p2##x = 0, _p1##x = 0, \
01235    _n1##x = 1>=(img)._width?(img).width()-1:1, \
01236    _n2##x = 2>=(img)._width?(img).width()-1:2, \
01237    _n3##x = (int)( \
01238    (I[0] = I[1] = I[2] = (T)(img)(0,_p2##y,z,c)), \
01239    (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
01240    (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
01241    (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
01242    (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
01243    (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
01244    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
01245    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
01246    (I[15] = (T)(img)(_n1##x,y,z,c)), \
01247    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
01248    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
01249    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
01250    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
01251    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
01252    (I[16] = (T)(img)(_n2##x,y,z,c)), \
01253    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
01254    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
01255    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
01256    3>=(img)._width?(img).width()-1:3); \
01257    (_n3##x<(img).width() && ( \
01258    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
01259    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
01260    (I[17] = (T)(img)(_n3##x,y,z,c)), \
01261    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
01262    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
01263    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
01264    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
01265    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
01266    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
01267    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
01268    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
01269    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
01270    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
01271    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
01272 
01273 #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
01274   cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
01275    _p2##x = x-2<0?0:x-2, \
01276    _p1##x = x-1<0?0:x-1, \
01277    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
01278    _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
01279    _n3##x = (int)( \
01280    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
01281    (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
01282    (I[12] = (T)(img)(_p2##x,y,z,c)), \
01283    (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
01284    (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
01285    (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
01286    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
01287    (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
01288    (I[13] = (T)(img)(_p1##x,y,z,c)), \
01289    (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
01290    (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
01291    (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
01292    (I[2] = (T)(img)(x,_p2##y,z,c)), \
01293    (I[8] = (T)(img)(x,_p1##y,z,c)), \
01294    (I[14] = (T)(img)(x,y,z,c)), \
01295    (I[20] = (T)(img)(x,_n1##y,z,c)), \
01296    (I[26] = (T)(img)(x,_n2##y,z,c)), \
01297    (I[32] = (T)(img)(x,_n3##y,z,c)), \
01298    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
01299    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
01300    (I[15] = (T)(img)(_n1##x,y,z,c)), \
01301    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
01302    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
01303    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
01304    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
01305    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
01306    (I[16] = (T)(img)(_n2##x,y,z,c)), \
01307    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
01308    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
01309    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
01310    x+3>=(int)(img)._width?(img).width()-1:x+3); \
01311    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
01312    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
01313    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
01314    (I[17] = (T)(img)(_n3##x,y,z,c)), \
01315    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
01316    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
01317    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
01318    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
01319    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
01320    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
01321    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
01322    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
01323    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
01324    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
01325    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
01326 
01327 #define cimg_for7x7(img,x,y,z,c,I,T) \
01328   cimg_for7((img)._height,y) for (int x = 0, \
01329    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
01330    _n1##x = 1>=(img)._width?(img).width()-1:1, \
01331    _n2##x = 2>=(img)._width?(img).width()-1:2, \
01332    _n3##x = (int)( \
01333    (I[0] = I[1] = I[2] = I[3] = (T)(img)(0,_p3##y,z,c)), \
01334    (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
01335    (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
01336    (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
01337    (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
01338    (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
01339    (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
01340    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
01341    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
01342    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
01343    (I[25] = (T)(img)(_n1##x,y,z,c)), \
01344    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
01345    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
01346    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
01347    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
01348    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
01349    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
01350    (I[26] = (T)(img)(_n2##x,y,z,c)), \
01351    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
01352    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
01353    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
01354    3>=(img)._width?(img).width()-1:3); \
01355    (_n3##x<(img).width() && ( \
01356    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
01357    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
01358    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
01359    (I[27] = (T)(img)(_n3##x,y,z,c)), \
01360    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
01361    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
01362    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
01363    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
01364    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
01365    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
01366    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
01367    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
01368    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
01369    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
01370    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
01371    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
01372 
01373 #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
01374   cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
01375    _p3##x = x-3<0?0:x-3, \
01376    _p2##x = x-2<0?0:x-2, \
01377    _p1##x = x-1<0?0:x-1, \
01378    _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
01379    _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
01380    _n3##x = (int)( \
01381    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
01382    (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
01383    (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
01384    (I[21] = (T)(img)(_p3##x,y,z,c)), \
01385    (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
01386    (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
01387    (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
01388    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
01389    (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
01390    (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
01391    (I[22] = (T)(img)(_p2##x,y,z,c)), \
01392    (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
01393    (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
01394    (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
01395    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
01396    (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
01397    (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
01398    (I[23] = (T)(img)(_p1##x,y,z,c)), \
01399    (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
01400    (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
01401    (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
01402    (I[3] = (T)(img)(x,_p3##y,z,c)), \
01403    (I[10] = (T)(img)(x,_p2##y,z,c)), \
01404    (I[17] = (T)(img)(x,_p1##y,z,c)), \
01405    (I[24] = (T)(img)(x,y,z,c)), \
01406    (I[31] = (T)(img)(x,_n1##y,z,c)), \
01407    (I[38] = (T)(img)(x,_n2##y,z,c)), \
01408    (I[45] = (T)(img)(x,_n3##y,z,c)), \
01409    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
01410    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
01411    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
01412    (I[25] = (T)(img)(_n1##x,y,z,c)), \
01413    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
01414    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
01415    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
01416    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
01417    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
01418    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
01419    (I[26] = (T)(img)(_n2##x,y,z,c)), \
01420    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
01421    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
01422    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
01423    x+3>=(int)(img)._width?(img).width()-1:x+3); \
01424    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
01425    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
01426    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
01427    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
01428    (I[27] = (T)(img)(_n3##x,y,z,c)), \
01429    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
01430    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
01431    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
01432    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
01433    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
01434    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
01435    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
01436    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
01437    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
01438    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
01439    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
01440    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
01441 
01442 #define cimg_for8x8(img,x,y,z,c,I,T) \
01443   cimg_for8((img)._height,y) for (int x = 0, \
01444    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
01445    _n1##x = 1>=((img)._width)?(img).width()-1:1, \
01446    _n2##x = 2>=((img)._width)?(img).width()-1:2, \
01447    _n3##x = 3>=((img)._width)?(img).width()-1:3, \
01448    _n4##x = (int)( \
01449    (I[0] = I[1] = I[2] = I[3] = (T)(img)(0,_p3##y,z,c)), \
01450    (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
01451    (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
01452    (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
01453    (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
01454    (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
01455    (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
01456    (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
01457    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
01458    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
01459    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
01460    (I[28] = (T)(img)(_n1##x,y,z,c)), \
01461    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
01462    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
01463    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
01464    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
01465    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
01466    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
01467    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
01468    (I[29] = (T)(img)(_n2##x,y,z,c)), \
01469    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
01470    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
01471    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
01472    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
01473    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
01474    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
01475    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
01476    (I[30] = (T)(img)(_n3##x,y,z,c)), \
01477    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
01478    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
01479    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
01480    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
01481    4>=((img)._width)?(img).width()-1:4); \
01482    (_n4##x<(img).width() && ( \
01483    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
01484    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
01485    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
01486    (I[31] = (T)(img)(_n4##x,y,z,c)), \
01487    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
01488    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
01489    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
01490    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
01491    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
01492    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \
01493    I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \
01494    I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
01495    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \
01496    I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \
01497    I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \
01498    I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \
01499    I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \
01500    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
01501 
01502 #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
01503   cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
01504    _p3##x = x-3<0?0:x-3, \
01505    _p2##x = x-2<0?0:x-2, \
01506    _p1##x = x-1<0?0:x-1, \
01507    _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
01508    _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
01509    _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
01510    _n4##x = (int)( \
01511    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
01512    (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
01513    (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
01514    (I[24] = (T)(img)(_p3##x,y,z,c)), \
01515    (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
01516    (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
01517    (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
01518    (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
01519    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
01520    (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
01521    (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
01522    (I[25] = (T)(img)(_p2##x,y,z,c)), \
01523    (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
01524    (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
01525    (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
01526    (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
01527    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
01528    (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
01529    (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
01530    (I[26] = (T)(img)(_p1##x,y,z,c)), \
01531    (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
01532    (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
01533    (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
01534    (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
01535    (I[3] = (T)(img)(x,_p3##y,z,c)), \
01536    (I[11] = (T)(img)(x,_p2##y,z,c)), \
01537    (I[19] = (T)(img)(x,_p1##y,z,c)), \
01538    (I[27] = (T)(img)(x,y,z,c)), \
01539    (I[35] = (T)(img)(x,_n1##y,z,c)), \
01540    (I[43] = (T)(img)(x,_n2##y,z,c)), \
01541    (I[51] = (T)(img)(x,_n3##y,z,c)), \
01542    (I[59] = (T)(img)(x,_n4##y,z,c)), \
01543    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
01544    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
01545    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
01546    (I[28] = (T)(img)(_n1##x,y,z,c)), \
01547    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
01548    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
01549    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
01550    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
01551    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
01552    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
01553    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
01554    (I[29] = (T)(img)(_n2##x,y,z,c)), \
01555    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
01556    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
01557    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
01558    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
01559    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
01560    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
01561    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
01562    (I[30] = (T)(img)(_n3##x,y,z,c)), \
01563    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
01564    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
01565    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
01566    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
01567    x+4>=(img).width()?(img).width()-1:x+4); \
01568    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
01569    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
01570    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
01571    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
01572    (I[31] = (T)(img)(_n4##x,y,z,c)), \
01573    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
01574    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
01575    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
01576    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
01577    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
01578    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \
01579    I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \
01580    I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
01581    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \
01582    I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \
01583    I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \
01584    I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \
01585    I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \
01586    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
01587 
01588 #define cimg_for9x9(img,x,y,z,c,I,T) \
01589   cimg_for9((img)._height,y) for (int x = 0, \
01590    _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
01591    _n1##x = 1>=((img)._width)?(img).width()-1:1, \
01592    _n2##x = 2>=((img)._width)?(img).width()-1:2, \
01593    _n3##x = 3>=((img)._width)?(img).width()-1:3, \
01594    _n4##x = (int)( \
01595    (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(0,_p4##y,z,c)), \
01596    (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
01597    (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
01598    (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
01599    (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
01600    (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
01601    (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
01602    (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
01603    (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
01604    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
01605    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
01606    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
01607    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
01608    (I[41] = (T)(img)(_n1##x,y,z,c)), \
01609    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
01610    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
01611    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
01612    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
01613    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
01614    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
01615    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
01616    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
01617    (I[42] = (T)(img)(_n2##x,y,z,c)), \
01618    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
01619    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
01620    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
01621    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
01622    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
01623    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
01624    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
01625    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
01626    (I[43] = (T)(img)(_n3##x,y,z,c)), \
01627    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
01628    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
01629    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
01630    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
01631    4>=((img)._width)?(img).width()-1:4); \
01632    (_n4##x<(img).width() && ( \
01633    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
01634    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
01635    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
01636    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
01637    (I[44] = (T)(img)(_n4##x,y,z,c)), \
01638    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
01639    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
01640    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
01641    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
01642    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
01643    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \
01644    I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
01645    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \
01646    I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
01647    I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \
01648    I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \
01649    I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \
01650    I[63] = I[64], I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \
01651    I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], I[79] = I[80], \
01652    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
01653 
01654 #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
01655   cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
01656    _p4##x = x-4<0?0:x-4, \
01657    _p3##x = x-3<0?0:x-3, \
01658    _p2##x = x-2<0?0:x-2, \
01659    _p1##x = x-1<0?0:x-1, \
01660    _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
01661    _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
01662    _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
01663    _n4##x = (int)( \
01664    (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
01665    (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
01666    (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
01667    (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
01668    (I[36] = (T)(img)(_p4##x,y,z,c)), \
01669    (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
01670    (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
01671    (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
01672    (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
01673    (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
01674    (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
01675    (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
01676    (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
01677    (I[37] = (T)(img)(_p3##x,y,z,c)), \
01678    (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
01679    (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
01680    (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
01681    (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
01682    (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
01683    (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
01684    (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
01685    (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
01686    (I[38] = (T)(img)(_p2##x,y,z,c)), \
01687    (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
01688    (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
01689    (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
01690    (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
01691    (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
01692    (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
01693    (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
01694    (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
01695    (I[39] = (T)(img)(_p1##x,y,z,c)), \
01696    (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
01697    (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
01698    (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
01699    (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
01700    (I[4] = (T)(img)(x,_p4##y,z,c)), \
01701    (I[13] = (T)(img)(x,_p3##y,z,c)), \
01702    (I[22] = (T)(img)(x,_p2##y,z,c)), \
01703    (I[31] = (T)(img)(x,_p1##y,z,c)), \
01704    (I[40] = (T)(img)(x,y,z,c)), \
01705    (I[49] = (T)(img)(x,_n1##y,z,c)), \
01706    (I[58] = (T)(img)(x,_n2##y,z,c)), \
01707    (I[67] = (T)(img)(x,_n3##y,z,c)), \
01708    (I[76] = (T)(img)(x,_n4##y,z,c)), \
01709    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
01710    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
01711    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
01712    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
01713    (I[41] = (T)(img)(_n1##x,y,z,c)), \
01714    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
01715    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
01716    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
01717    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
01718    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
01719    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
01720    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
01721    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
01722    (I[42] = (T)(img)(_n2##x,y,z,c)), \
01723    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
01724    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
01725    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
01726    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
01727    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
01728    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
01729    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
01730    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
01731    (I[43] = (T)(img)(_n3##x,y,z,c)), \
01732    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
01733    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
01734    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
01735    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
01736    x+4>=(img).width()?(img).width()-1:x+4); \
01737    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
01738    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
01739    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
01740    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
01741    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
01742    (I[44] = (T)(img)(_n4##x,y,z,c)), \
01743    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
01744    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
01745    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
01746    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
01747    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
01748    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \
01749    I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
01750    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \
01751    I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
01752    I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \
01753    I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \
01754    I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \
01755    I[63] = I[64], I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \
01756    I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], I[79] = I[80], \
01757    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
01758 
01759 #define cimg_for2x2x2(img,x,y,z,c,I,T) \
01760  cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
01761    _n1##x = (int)( \
01762    (I[0] = (T)(img)(0,y,z,c)), \
01763    (I[2] = (T)(img)(0,_n1##y,z,c)), \
01764    (I[4] = (T)(img)(0,y,_n1##z,c)), \
01765    (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
01766    1>=(img)._width?(img).width()-1:1); \
01767    (_n1##x<(img).width() && ( \
01768    (I[1] = (T)(img)(_n1##x,y,z,c)), \
01769    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
01770    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
01771    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
01772    x==--_n1##x; \
01773    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
01774    ++x, ++_n1##x)
01775 
01776 #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
01777  cimg_for_in2((img)._depth,z0,z1,z) cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
01778    _n1##x = (int)( \
01779    (I[0] = (T)(img)(x,y,z,c)), \
01780    (I[2] = (T)(img)(x,_n1##y,z,c)), \
01781    (I[4] = (T)(img)(x,y,_n1##z,c)), \
01782    (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
01783    x+1>=(int)(img)._width?(img).width()-1:x+1); \
01784    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
01785    (I[1] = (T)(img)(_n1##x,y,z,c)), \
01786    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
01787    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
01788    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
01789    x==--_n1##x); \
01790    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
01791    ++x, ++_n1##x)
01792 
01793 #define cimg_for3x3x3(img,x,y,z,c,I,T) \
01794  cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
01795    _p1##x = 0, \
01796    _n1##x = (int)( \
01797    (I[0] = I[1] = (T)(img)(0,_p1##y,_p1##z,c)), \
01798    (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)),  \
01799    (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
01800    (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
01801    (I[12] = I[13] = (T)(img)(0,y,z,c)), \
01802    (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
01803    (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
01804    (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
01805    (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
01806    1>=(img)._width?(img).width()-1:1); \
01807    (_n1##x<(img).width() && ( \
01808    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
01809    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
01810    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
01811    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
01812    (I[14] = (T)(img)(_n1##x,y,z,c)), \
01813    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
01814    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
01815    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
01816    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
01817    x==--_n1##x; \
01818    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
01819    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
01820    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
01821    _p1##x = x++, ++_n1##x)
01822 
01823 #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
01824  cimg_for_in3((img)._depth,z0,z1,z) cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
01825    _p1##x = x-1<0?0:x-1, \
01826    _n1##x = (int)( \
01827    (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
01828    (I[3] = (T)(img)(_p1##x,y,_p1##z,c)),  \
01829    (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
01830    (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
01831    (I[12] = (T)(img)(_p1##x,y,z,c)), \
01832    (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
01833    (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
01834    (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
01835    (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
01836    (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
01837    (I[4] = (T)(img)(x,y,_p1##z,c)),  \
01838    (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
01839    (I[10] = (T)(img)(x,_p1##y,z,c)), \
01840    (I[13] = (T)(img)(x,y,z,c)), \
01841    (I[16] = (T)(img)(x,_n1##y,z,c)), \
01842    (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
01843    (I[22] = (T)(img)(x,y,_n1##z,c)), \
01844    (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
01845    x+1>=(int)(img)._width?(img).width()-1:x+1); \
01846    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
01847    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
01848    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
01849    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
01850    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
01851    (I[14] = (T)(img)(_n1##x,y,z,c)), \
01852    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
01853    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
01854    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
01855    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
01856    x==--_n1##x); \
01857    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
01858    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
01859    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
01860    _p1##x = x++, ++_n1##x)
01861 
01862 #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
01863 #define cimglist_for_in(list,l0,l1,l) \
01864   for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width-1; l<=_max##l; ++l)
01865 
01866 #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
01867 
01868 // Define macros used when exceptions are thrown.
01869 #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
01870 #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
01871 #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
01872 #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
01873 #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
01874 #define cimglist_instance _width,_allocated_width,_data,pixel_type()
01875 
01876 /*------------------------------------------------
01877  #
01878  #
01879  #  Definition of the cimg_library:: namespace
01880  #
01881  #
01882  -------------------------------------------------*/
01883 //! This namespace encompasses all classes and functions of the %CImg library.
01884 /**
01885    This namespace is defined to avoid functions and class names collisions
01886    that could happen with the include of other C++ header files.
01887    Anyway, it should not happen often and you should reasonnably start most of your
01888    %CImg-based programs with
01889    \code
01890    #include "CImg.h"
01891    using namespace cimg_library;
01892    \endcode
01893    to simplify the declaration of %CImg Library variables afterwards.
01894 **/
01895 namespace cimg_library {
01896 
01897   // Declare the only four classes of the CImg Library.
01898   template<typename T=float> struct CImg;
01899   template<typename T=float> struct CImgList;
01900   struct CImgDisplay;
01901   struct CImgException;
01902 
01903   // (Pre)declare the cimg namespace.
01904   // This is not the complete namespace declaration. It only contains some
01905   // necessary stuffs to ensure a correct declaration order of classes and functions
01906   // defined afterwards.
01907   namespace cimg {
01908 
01909 #ifdef cimg_use_vt100
01910     const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
01911     const char t_red[] = { 0x1b, '[', '4', ';', '3', '1', ';', '5', '9', 'm', 0 };
01912     const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
01913     const char t_purple[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
01914     const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
01915 #else
01916     const char t_normal[] = { 0 };
01917     const char *const t_red = cimg::t_normal, *const t_bold = cimg::t_normal,
01918       *const t_purple = cimg::t_normal, *const t_green = cimg::t_normal;
01919 #endif
01920 
01921     inline std::FILE* output(std::FILE *file=0);
01922     inline void info();
01923 
01924     // Function used to avoid warning messages due to unused parameters.
01925     template<typename T>
01926     inline void unused(const T&, ...) {}
01927 
01928     //! Get/set the current CImg exception mode.
01929     /**
01930        The way error messages are handled by CImg can be changed dynamically, using this function.
01931        Possible values are :
01932        - '0' to hide library messages (quiet mode).
01933        - '1' to print library messages on the console.
01934        - '2' to display library messages on a dialog window (default behavior).
01935        - '3' to do as '1' + add extra warnings (may slow down the code !).
01936        - '4' to do as '2' + add extra warnings (may slow down the code !).
01937      **/
01938     inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) {
01939       static unsigned int mode = cimg_verbosity;
01940       if (is_set) mode = value;
01941       return mode;
01942     }
01943     inline unsigned int& exception_mode() {
01944       return _exception_mode(0,false);
01945     }
01946     inline unsigned int& exception_mode(const unsigned int mode) {
01947       return _exception_mode(mode,true);
01948     }
01949 
01950     inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK",
01951                       const char *const button2_label=0, const char *const button3_label=0,
01952                       const char *const button4_label=0, const char *const button5_label=0,
01953                       const char *const button6_label=0, const bool centering=false);
01954 
01955     //! Evaluate math expression.
01956     inline double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double v=0);
01957   }
01958 
01959   /*----------------------------------------------
01960    #
01961    # Definition of the CImgException structures
01962    #
01963    ----------------------------------------------*/
01964   //! Instances of this class are thrown when errors occur during a %CImg library function call.
01965   /**
01966      \section ex1 Overview
01967 
01968       CImgException is the base class of %CImg exceptions.
01969       Exceptions are thrown by the %CImg Library when an error occured in a %CImg library function call.
01970       CImgException is seldom thrown itself. Children classes that specify the kind of error encountered
01971       are generally used instead. These sub-classes are :
01972 
01973       - \b CImgInstanceException : Thrown when the instance associated to the called %CImg function is not
01974       correctly defined. Generally, this exception is thrown when one tries to process \a empty images. The example
01975       below will throw a \a CImgInstanceException.
01976       \code
01977       CImg<float> img;        // Construct an empty image.
01978       img.blur(10);           // Try to blur the image.
01979       \endcode
01980 
01981       - \b CImgArgumentException : Thrown when one of the arguments given to the called %CImg function is not correct.
01982       Generally, this exception is thrown when arguments passed to the function are outside an admissible range of values.
01983       The example below will throw a \a CImgArgumentException.
01984       \code
01985       CImg<float> img(100,100,1,3);   // Define a 100x100 color image with float pixels.
01986       img = 0;                     // Try to fill pixels from the 0 pointer (invalid argument to operator=() ).
01987       \endcode
01988 
01989       - \b CImgIOException : Thrown when an error occured when trying to load or save image files.
01990       The example below will throw a \a CImgIOException.
01991       \code
01992       CImg<float> img("file_doesnt_exist.jpg");    // Try to load a file that doesn't exist.
01993       \endcode
01994 
01995       - \b CImgDisplayException : Thrown when an error occured when trying to display an image in a window.
01996       This exception is thrown when image display request cannot be satisfied.
01997 
01998       The parent class CImgException may be thrown itself when errors that cannot be classified in one of
01999       the above type occur. It is recommended not to throw CImgExceptions yourself, since there are normally
02000       reserved to %CImg Library functions.
02001       \b CImgInstanceException, \b CImgArgumentException, \b CImgIOException and \b CImgDisplayException are simple
02002       subclasses of CImgException and are thus not detailled more in this reference documentation.
02003 
02004       \section ex2 Exception handling
02005 
02006       When an error occurs, the %CImg Library first displays the error in a modal window.
02007       Then, it throws an instance of the corresponding exception class, generally leading the program to stop
02008       (this is the default behavior).
02009       You can bypass this default behavior by handling the exceptions yourself,
02010       using a code block <tt>try { ... } catch () { ... }</tt>.
02011       In this case, you can avoid the apparition of the modal window, by
02012       defining the environment variable <tt>cimg_verbosity</tt> to 0 before including the %CImg header file.
02013       The example below shows how to cleanly handle %CImg Library exceptions :
02014       \code
02015       #define cimg_verbosity 0     // Disable modal window in CImg exceptions.
02016       #define "CImg.h"
02017       int main() {
02018         try {
02019           ...; // Here, do what you want.
02020         }
02021         catch (CImgInstanceException &e) {
02022           std::fprintf(stderr,"CImg Library Error : %s",e.what());  // Display your own error message
02023           ...                                                       // Do what you want now.
02024         }
02025       }
02026       \endcode
02027   **/
02028   struct CImgException : public std::exception {
02029 #define _cimg_exception_err(etype,disp_flag) \
02030   std::va_list ap; va_start(ap,format); std::vsprintf(_message,format,ap); va_end(ap); \
02031   if (cimg::exception_mode()) { \
02032     std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
02033     if (cimg_display && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } catch (CImgException&) {} \
02034     if (cimg::exception_mode()>=3) cimg_library::cimg::info(); \
02035   }
02036 
02037     char _message[16384];
02038     CImgException() { *_message = 0; }
02039     CImgException(const char *const format, ...) { _cimg_exception_err("CImgException",true); }
02040     const char *what() const throw() { return _message; }
02041   };
02042 
02043   // The \ref CImgInstanceException class is used to throw an exception related
02044   // to a non suitable instance encountered in a library function call.
02045   struct CImgInstanceException : public CImgException {
02046     CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
02047   };
02048 
02049   // The \ref CImgArgumentException class is used to throw an exception related
02050   // to invalid arguments encountered in a library function call.
02051   struct CImgArgumentException : public CImgException {
02052     CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
02053   };
02054 
02055   // The \ref CImgIOException class is used to throw an exception related
02056   // to Input/Output file problems encountered in a library function call.
02057   struct CImgIOException : public CImgException {
02058     CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
02059   };
02060 
02061   // The CImgDisplayException class is used to throw an exception related to display problems
02062   // encountered in a library function call.
02063   struct CImgDisplayException : public CImgException {
02064     CImgDisplayException(const char *const format, ...) {
02065       const unsigned int current_mode = cimg::exception_mode();
02066       cimg::exception_mode(current_mode==2?1:current_mode==4?3:current_mode);
02067       _cimg_exception_err("CImgDisplayException",false);
02068     }
02069   };
02070 
02071   // The CImgWarningException class is used to throw an exception for warnings
02072   // encountered in a library function call.
02073   struct CImgWarningException : public CImgException {
02074     CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
02075   };
02076 
02077   /*-------------------------------------
02078    #
02079    # Definition of the namespace 'cimg'
02080    #
02081    --------------------------------------*/
02082   //! Namespace that encompasses \a low-level functions and variables of the %CImg Library.
02083   /**
02084      Most of the functions and variables within this namespace are used by the library for low-level processing.
02085      Nevertheless, documented variables and functions of this namespace may be used safely in your own source code.
02086 
02087      \warning Never write <tt>using namespace cimg_library::cimg;</tt> in your source code, since a lot of functions of the
02088      <tt>cimg::</tt> namespace have prototypes similar to standard C functions that could defined in the global namespace <tt>::</tt>.
02089   **/
02090   namespace cimg {
02091 
02092     // Define the traits that will be used to determine the best data type to work with.
02093     //
02094     template<typename T> struct type {
02095       static const char* string() {
02096         static const char* s[] = { "unknown",   "unknown8",   "unknown16",  "unknown24",
02097                                    "unknown32", "unknown40",  "unknown48",  "unknown56",
02098                                    "unknown64", "unknown72",  "unknown80",  "unknown88",
02099                                    "unknown96", "unknown104", "unknown112", "unknown120",
02100                                    "unknown128" };
02101         return s[(sizeof(T)<17)?sizeof(T):0];
02102       }
02103       static bool is_float() { return false; }
02104       static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); }
02105       static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); }
02106       static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
02107       static const char* format() { return "%s"; }
02108       static const char* format(const T val) { static const char *const s = "unknown"; return s; }
02109     };
02110 
02111     template<> struct type<bool> {
02112       static const char* string() { static const char *const s = "bool"; return s; }
02113       static bool is_float() { return false; }
02114       static bool min() { return false; }
02115       static bool max() { return true; }
02116       static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
02117       static const char* format() { return "%s"; }
02118       static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
02119     };
02120 
02121     template<> struct type<unsigned char> {
02122       static const char* string() { static const char *const s = "unsigned char"; return s; }
02123       static bool is_float() { return false; }
02124       static unsigned char min() { return 0; }
02125       static unsigned char max() { return (unsigned char)~0U; }
02126       static unsigned char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
02127       static const char* format() { return "%u"; }
02128       static unsigned int format(const unsigned char val) { return (unsigned int)val; }
02129     };
02130 
02131     template<> struct type<char> {
02132       static const char* string() { static const char *const s = "char"; return s; }
02133       static bool is_float() { return false; }
02134       static char min() { return (char)(-1L<<(8*sizeof(char)-1)); }
02135       static char max() { return ~((char)(-1L<<(8*sizeof(char)-1))); }
02136       static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
02137       static const char* format() { return "%d"; }
02138       static int format(const char val) { return (int)val; }
02139     };
02140 
02141     template<> struct type<signed char> {
02142       static const char* string() { static const char *const s = "signed char"; return s; }
02143       static bool is_float() { return false; }
02144       static signed char min() { return (signed char)(-1L<<(8*sizeof(signed char)-1)); }
02145       static signed char max() { return ~((signed char)(-1L<<(8*sizeof(signed char)-1))); }
02146       static signed char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
02147       static const char* format() { return "%d"; }
02148       static unsigned int format(const signed char val) { return (int)val; }
02149     };
02150 
02151     template<> struct type<unsigned short> {
02152       static const char* string() { static const char *const s = "unsigned short"; return s; }
02153       static bool is_float() { return false; }
02154       static unsigned short min() { return 0; }
02155       static unsigned short max() { return (unsigned short)~0U; }
02156       static unsigned short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
02157       static const char* format() { return "%u"; }
02158       static unsigned int format(const unsigned short val) { return (unsigned int)val; }
02159     };
02160 
02161     template<> struct type<short> {
02162       static const char* string() { static const char *const s = "short"; return s; }
02163       static bool is_float() { return false; }
02164       static short min() { return (short)(-1L<<(8*sizeof(short)-1)); }
02165       static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); }
02166       static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
02167       static const char* format() { return "%d"; }
02168       static int format(const short val) { return (int)val; }
02169     };
02170 
02171     template<> struct type<unsigned int> {
02172       static const char* string() { static const char *const s = "unsigned int"; return s; }
02173       static bool is_float() { return false; }
02174       static unsigned int min() { return 0; }
02175       static unsigned int max() { return (unsigned int)~0U; }
02176       static unsigned int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
02177       static const char* format() { return "%u"; }
02178       static unsigned int format(const unsigned int val) { return val; }
02179     };
02180 
02181     template<> struct type<int> {
02182       static const char* string() { static const char *const s = "int"; return s; }
02183       static bool is_float() { return false; }
02184       static int min() { return (int)(-1L<<(8*sizeof(int)-1)); }
02185       static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); }
02186       static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
02187       static const char* format() { return "%d"; }
02188       static int format(const int val) { return val; }
02189     };
02190 
02191     template<> struct type<unsigned long> {
02192       static const char* string() { static const char *const s = "unsigned long"; return s; }
02193       static bool is_float() { return false; }
02194       static unsigned long min() { return 0; }
02195       static unsigned long max() { return (unsigned long)~0UL; }
02196       static unsigned long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned long)val; }
02197       static const char* format() { return "%lu"; }
02198       static unsigned long format(const unsigned long val) { return val; }
02199     };
02200 
02201     template<> struct type<long> {
02202       static const char* string() { static const char *const s = "long"; return s; }
02203       static bool is_float() { return false; }
02204       static long min() { return (long)(-1L<<(8*sizeof(long)-1)); }
02205       static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); }
02206       static long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(long)val; }
02207       static const char* format() { return "%ld"; }
02208       static long format(const long val) { return val; }
02209     };
02210 
02211     template<> struct type<double> {
02212       static const char* string() { static const char *const s = "double"; return s; }
02213       static bool is_float() { return true; }
02214       static double min() { return -1.7E308; }
02215       static double max() { return  1.7E308; }
02216       static double cut(const double val) { return val<min()?min():val>max()?max():val; }
02217       static double inf() { return max()*max(); }
02218       static double nan() { static const double v_nan = std::sqrt(-1.0); return v_nan; }
02219       static const char* format() { return "%g"; }
02220       static double format(const double val) { return val; }
02221     };
02222 
02223     template<> struct type<float> {
02224       static const char* string() { static const char *const s = "float"; return s; }
02225       static bool is_float() { return true; }
02226       static float min() { return -3.4E38f; }
02227       static float max() { return  3.4E38f; }
02228       static float cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(float)val; }
02229       static float inf() { return (float)cimg::type<double>::inf(); }
02230       static float nan() { return (float)cimg::type<double>::nan(); }
02231       static const char* format() { return "%g"; }
02232       static double format(const float val) { return (double)val; }
02233     };
02234 
02235     template<typename T, typename t> struct superset { typedef T type; };
02236     template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
02237     template<> struct superset<bool,char> { typedef char type; };
02238     template<> struct superset<bool,signed char> { typedef signed char type; };
02239     template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
02240     template<> struct superset<bool,short> { typedef short type; };
02241     template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
02242     template<> struct superset<bool,int> { typedef int type; };
02243     template<> struct superset<bool,unsigned long> { typedef unsigned long type; };
02244     template<> struct superset<bool,long> { typedef long type; };
02245     template<> struct superset<bool,float> { typedef float type; };
02246     template<> struct superset<bool,double> { typedef double type; };
02247     template<> struct superset<unsigned char,char> { typedef short type; };
02248     template<> struct superset<unsigned char,signed char> { typedef short type; };
02249     template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
02250     template<> struct superset<unsigned char,short> { typedef short type; };
02251     template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
02252     template<> struct superset<unsigned char,int> { typedef int type; };
02253     template<> struct superset<unsigned char,unsigned long> { typedef unsigned long type; };
02254     template<> struct superset<unsigned char,long> { typedef long type; };
02255     template<> struct superset<unsigned char,float> { typedef float type; };
02256     template<> struct superset<unsigned char,double> { typedef double type; };
02257     template<> struct superset<signed char,unsigned char> { typedef short type; };
02258     template<> struct superset<signed char,char> { typedef short type; };
02259     template<> struct superset<signed char,unsigned short> { typedef int type; };
02260     template<> struct superset<signed char,short> { typedef short type; };
02261     template<> struct superset<signed char,unsigned int> { typedef long type; };
02262     template<> struct superset<signed char,int> { typedef int type; };
02263     template<> struct superset<signed char,unsigned long> { typedef long type; };
02264     template<> struct superset<signed char,long> { typedef long type; };
02265     template<> struct superset<signed char,float> { typedef float type; };
02266     template<> struct superset<signed char,double> { typedef double type; };
02267     template<> struct superset<char,unsigned char> { typedef short type; };
02268     template<> struct superset<char,signed char> { typedef short type; };
02269     template<> struct superset<char,unsigned short> { typedef int type; };
02270     template<> struct superset<char,short> { typedef short type; };
02271     template<> struct superset<char,unsigned int> { typedef long type; };
02272     template<> struct superset<char,int> { typedef int type; };
02273     template<> struct superset<char,unsigned long> { typedef long type; };
02274     template<> struct superset<char,long> { typedef long type; };
02275     template<> struct superset<char,float> { typedef float type; };
02276     template<> struct superset<char,double> { typedef double type; };
02277     template<> struct superset<unsigned short,char> { typedef int type; };
02278     template<> struct superset<unsigned short,signed char> { typedef int type; };
02279     template<> struct superset<unsigned short,short> { typedef int type; };
02280     template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
02281     template<> struct superset<unsigned short,int> { typedef int type; };
02282     template<> struct superset<unsigned short,unsigned long> { typedef unsigned long type; };
02283     template<> struct superset<unsigned short,long> { typedef long type; };
02284     template<> struct superset<unsigned short,float> { typedef float type; };
02285     template<> struct superset<unsigned short,double> { typedef double type; };
02286     template<> struct superset<short,unsigned short> { typedef int type; };
02287     template<> struct superset<short,unsigned int> { typedef long type; };
02288     template<> struct superset<short,int> { typedef int type; };
02289     template<> struct superset<short,unsigned long> { typedef long type; };
02290     template<> struct superset<short,long> { typedef long type; };
02291     template<> struct superset<short,float> { typedef float type; };
02292     template<> struct superset<short,double> { typedef double type; };
02293     template<> struct superset<unsigned int,char> { typedef long type; };
02294     template<> struct superset<unsigned int,signed char> { typedef long type; };
02295     template<> struct superset<unsigned int,short> { typedef long type; };
02296     template<> struct superset<unsigned int,int> { typedef long type; };
02297     template<> struct superset<unsigned int,unsigned long> { typedef unsigned long type; };
02298     template<> struct superset<unsigned int,long> { typedef long type; };
02299     template<> struct superset<unsigned int,float> { typedef float type; };
02300     template<> struct superset<unsigned int,double> { typedef double type; };
02301     template<> struct superset<int,unsigned int> { typedef long type; };
02302     template<> struct superset<int,unsigned long> { typedef long type; };
02303     template<> struct superset<int,long> { typedef long type; };
02304     template<> struct superset<int,float> { typedef float type; };
02305     template<> struct superset<int,double> { typedef double type; };
02306     template<> struct superset<unsigned long,char> { typedef long type; };
02307     template<> struct superset<unsigned long,signed char> { typedef long type; };
02308     template<> struct superset<unsigned long,short> { typedef long type; };
02309     template<> struct superset<unsigned long,int> { typedef long type; };
02310     template<> struct superset<unsigned long,long> { typedef long type; };
02311     template<> struct superset<unsigned long,float> { typedef float type; };
02312     template<> struct superset<unsigned long,double> { typedef double type; };
02313     template<> struct superset<long,float> { typedef float type; };
02314     template<> struct superset<long,double> { typedef double type; };
02315     template<> struct superset<float,double> { typedef double type; };
02316 
02317     template<typename t1, typename t2, typename t3> struct superset2 {
02318       typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
02319     };
02320 
02321     template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
02322       typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
02323     };
02324 
02325     template<typename t1, typename t2> struct last { typedef t2 type; };
02326 
02327 #define _cimg_Tt      typename cimg::superset<T,t>::type
02328 #define _cimg_Tfloat  typename cimg::superset<T,float>::type
02329 #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
02330 
02331     // Define internal library variables.
02332 #if cimg_display==1
02333     struct X11_info {
02334       volatile unsigned int nb_wins;
02335       pthread_t*       event_thread;
02336       CImgDisplay*     wins[1024];
02337       Display*         display;
02338       unsigned int     nb_bits;
02339       GC*              gc;
02340       bool             blue_first;
02341       bool             byte_order;
02342       bool             shm_enabled;
02343 #ifdef cimg_use_xrandr
02344       XRRScreenSize *resolutions;
02345       Rotation curr_rotation;
02346       unsigned int curr_resolution;
02347       unsigned int nb_resolutions;
02348 #endif
02349       X11_info():nb_wins(0),event_thread(0),display(0),
02350                 nb_bits(0),gc(0),blue_first(false),byte_order(false),shm_enabled(false) {
02351 #ifdef cimg_use_xrandr
02352         resolutions = 0;
02353         curr_rotation = 0;
02354         curr_resolution = nb_resolutions = 0;
02355 #endif
02356       }
02357     };
02358 #if defined(cimg_module)
02359     X11_info& X11_attr();
02360 #elif defined(cimg_main)
02361     X11_info& X11_attr() { static X11_info val; return val; }
02362 #else
02363     inline X11_info& X11_attr() { static X11_info val; return val; }
02364 #endif
02365 
02366 #elif cimg_display==2
02367     struct Win32_info {
02368       HANDLE wait_event;
02369       Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); }
02370     };
02371 #if defined(cimg_module)
02372     Win32_info& Win32_attr();
02373 #elif defined(cimg_main)
02374     Win32_info& Win32_attr() { static Win32_info val; return val; }
02375 #else
02376     inline Win32_info& Win32_attr() { static Win32_info val; return val; }
02377 #endif
02378 #endif
02379 
02380 #if cimg_display==1
02381     // Keycodes for X11-based graphical systems.
02382     const unsigned int keyESC        = XK_Escape;
02383     const unsigned int keyF1         = XK_F1;
02384     const unsigned int keyF2         = XK_F2;
02385     const unsigned int keyF3         = XK_F3;
02386     const unsigned int keyF4         = XK_F4;
02387     const unsigned int keyF5         = XK_F5;
02388     const unsigned int keyF6         = XK_F6;
02389     const unsigned int keyF7         = XK_F7;
02390     const unsigned int keyF8         = XK_F8;
02391     const unsigned int keyF9         = XK_F9;
02392     const unsigned int keyF10        = XK_F10;
02393     const unsigned int keyF11        = XK_F11;
02394     const unsigned int keyF12        = XK_F12;
02395     const unsigned int keyPAUSE      = XK_Pause;
02396     const unsigned int key1          = XK_1;
02397     const unsigned int key2          = XK_2;
02398     const unsigned int key3          = XK_3;
02399     const unsigned int key4          = XK_4;
02400     const unsigned int key5          = XK_5;
02401     const unsigned int key6          = XK_6;
02402     const unsigned int key7          = XK_7;
02403     const unsigned int key8          = XK_8;
02404     const unsigned int key9          = XK_9;
02405     const unsigned int key0          = XK_0;
02406     const unsigned int keyBACKSPACE  = XK_BackSpace;
02407     const unsigned int keyINSERT     = XK_Insert;
02408     const unsigned int keyHOME       = XK_Home;
02409     const unsigned int keyPAGEUP     = XK_Page_Up;
02410     const unsigned int keyTAB        = XK_Tab;
02411     const unsigned int keyQ          = XK_q;
02412     const unsigned int keyW          = XK_w;
02413     const unsigned int keyE          = XK_e;
02414     const unsigned int keyR          = XK_r;
02415     const unsigned int keyT          = XK_t;
02416     const unsigned int keyY          = XK_y;
02417     const unsigned int keyU          = XK_u;
02418     const unsigned int keyI          = XK_i;
02419     const unsigned int keyO          = XK_o;
02420     const unsigned int keyP          = XK_p;
02421     const unsigned int keyDELETE     = XK_Delete;
02422     const unsigned int keyEND        = XK_End;
02423     const unsigned int keyPAGEDOWN   = XK_Page_Down;
02424     const unsigned int keyCAPSLOCK   = XK_Caps_Lock;
02425     const unsigned int keyA          = XK_a;
02426     const unsigned int keyS          = XK_s;
02427     const unsigned int keyD          = XK_d;
02428     const unsigned int keyF          = XK_f;
02429     const unsigned int keyG          = XK_g;
02430     const unsigned int keyH          = XK_h;
02431     const unsigned int keyJ          = XK_j;
02432     const unsigned int keyK          = XK_k;
02433     const unsigned int keyL          = XK_l;
02434     const unsigned int keyENTER      = XK_Return;
02435     const unsigned int keySHIFTLEFT  = XK_Shift_L;
02436     const unsigned int keyZ          = XK_z;
02437     const unsigned int keyX          = XK_x;
02438     const unsigned int keyC          = XK_c;
02439     const unsigned int keyV          = XK_v;
02440     const unsigned int keyB          = XK_b;
02441     const unsigned int keyN          = XK_n;
02442     const unsigned int keyM          = XK_m;
02443     const unsigned int keySHIFTRIGHT = XK_Shift_R;
02444     const unsigned int keyARROWUP    = XK_Up;
02445     const unsigned int keyCTRLLEFT   = XK_Control_L;
02446     const unsigned int keyAPPLEFT    = XK_Super_L;
02447     const unsigned int keyALT        = XK_Alt_L;
02448     const unsigned int keySPACE      = XK_space;
02449     const unsigned int keyALTGR      = XK_Alt_R;
02450     const unsigned int keyAPPRIGHT   = XK_Super_R;
02451     const unsigned int keyMENU       = XK_Menu;
02452     const unsigned int keyCTRLRIGHT  = XK_Control_R;
02453     const unsigned int keyARROWLEFT  = XK_Left;
02454     const unsigned int keyARROWDOWN  = XK_Down;
02455     const unsigned int keyARROWRIGHT = XK_Right;
02456     const unsigned int keyPAD0       = XK_KP_0;
02457     const unsigned int keyPAD1       = XK_KP_1;
02458     const unsigned int keyPAD2       = XK_KP_2;
02459     const unsigned int keyPAD3       = XK_KP_3;
02460     const unsigned int keyPAD4       = XK_KP_4;
02461     const unsigned int keyPAD5       = XK_KP_5;
02462     const unsigned int keyPAD6       = XK_KP_6;
02463     const unsigned int keyPAD7       = XK_KP_7;
02464     const unsigned int keyPAD8       = XK_KP_8;
02465     const unsigned int keyPAD9       = XK_KP_9;
02466     const unsigned int keyPADADD     = XK_KP_Add;
02467     const unsigned int keyPADSUB     = XK_KP_Subtract;
02468     const unsigned int keyPADMUL     = XK_KP_Multiply;
02469     const unsigned int keyPADDIV     = XK_KP_Divide;
02470 
02471 #elif cimg_display==2
02472     // Keycodes for Windows.
02473     const unsigned int keyESC        = VK_ESCAPE;
02474     const unsigned int keyF1         = VK_F1;
02475     const unsigned int keyF2         = VK_F2;
02476     const unsigned int keyF3         = VK_F3;
02477     const unsigned int keyF4         = VK_F4;
02478     const unsigned int keyF5         = VK_F5;
02479     const unsigned int keyF6         = VK_F6;
02480     const unsigned int keyF7         = VK_F7;
02481     const unsigned int keyF8         = VK_F8;
02482     const unsigned int keyF9         = VK_F9;
02483     const unsigned int keyF10        = VK_F10;
02484     const unsigned int keyF11        = VK_F11;
02485     const unsigned int keyF12        = VK_F12;
02486     const unsigned int keyPAUSE      = VK_PAUSE;
02487     const unsigned int key1          = '1';
02488     const unsigned int key2          = '2';
02489     const unsigned int key3          = '3';
02490     const unsigned int key4          = '4';
02491     const unsigned int key5          = '5';
02492     const unsigned int key6          = '6';
02493     const unsigned int key7          = '7';
02494     const unsigned int key8          = '8';
02495     const unsigned int key9          = '9';
02496     const unsigned int key0          = '0';
02497     const unsigned int keyBACKSPACE  = VK_BACK;
02498     const unsigned int keyINSERT     = VK_INSERT;
02499     const unsigned int keyHOME       = VK_HOME;
02500     const unsigned int keyPAGEUP     = VK_PRIOR;
02501     const unsigned int keyTAB        = VK_TAB;
02502     const unsigned int keyQ          = 'Q';
02503     const unsigned int keyW          = 'W';
02504     const unsigned int keyE          = 'E';
02505     const unsigned int keyR          = 'R';
02506     const unsigned int keyT          = 'T';
02507     const unsigned int keyY          = 'Y';
02508     const unsigned int keyU          = 'U';
02509     const unsigned int keyI          = 'I';
02510     const unsigned int keyO          = 'O';
02511     const unsigned int keyP          = 'P';
02512     const unsigned int keyDELETE     = VK_DELETE;
02513     const unsigned int keyEND        = VK_END;
02514     const unsigned int keyPAGEDOWN   = VK_NEXT;
02515     const unsigned int keyCAPSLOCK   = VK_CAPITAL;
02516     const unsigned int keyA          = 'A';
02517     const unsigned int keyS          = 'S';
02518     const unsigned int keyD          = 'D';
02519     const unsigned int keyF          = 'F';
02520     const unsigned int keyG          = 'G';
02521     const unsigned int keyH          = 'H';
02522     const unsigned int keyJ          = 'J';
02523     const unsigned int keyK          = 'K';
02524     const unsigned int keyL          = 'L';
02525     const unsigned int keyENTER      = VK_RETURN;
02526     const unsigned int keySHIFTLEFT  = VK_SHIFT;
02527     const unsigned int keyZ          = 'Z';
02528     const unsigned int keyX          = 'X';
02529     const unsigned int keyC          = 'C';
02530     const unsigned int keyV          = 'V';
02531     const unsigned int keyB          = 'B';
02532     const unsigned int keyN          = 'N';
02533     const unsigned int keyM          = 'M';
02534     const unsigned int keySHIFTRIGHT = VK_SHIFT;
02535     const unsigned int keyARROWUP    = VK_UP;
02536     const unsigned int keyCTRLLEFT   = VK_CONTROL;
02537     const unsigned int keyAPPLEFT    = VK_LWIN;
02538     const unsigned int keyALT        = VK_LMENU;
02539     const unsigned int keySPACE      = VK_SPACE;
02540     const unsigned int keyALTGR      = VK_CONTROL;
02541     const unsigned int keyAPPRIGHT   = VK_RWIN;
02542     const unsigned int keyMENU       = VK_APPS;
02543     const unsigned int keyCTRLRIGHT  = VK_CONTROL;
02544     const unsigned int keyARROWLEFT  = VK_LEFT;
02545     const unsigned int keyARROWDOWN  = VK_DOWN;
02546     const unsigned int keyARROWRIGHT = VK_RIGHT;
02547     const unsigned int keyPAD0       = 0x60;
02548     const unsigned int keyPAD1       = 0x61;
02549     const unsigned int keyPAD2       = 0x62;
02550     const unsigned int keyPAD3       = 0x63;
02551     const unsigned int keyPAD4       = 0x64;
02552     const unsigned int keyPAD5       = 0x65;
02553     const unsigned int keyPAD6       = 0x66;
02554     const unsigned int keyPAD7       = 0x67;
02555     const unsigned int keyPAD8       = 0x68;
02556     const unsigned int keyPAD9       = 0x69;
02557     const unsigned int keyPADADD     = VK_ADD;
02558     const unsigned int keyPADSUB     = VK_SUBTRACT;
02559     const unsigned int keyPADMUL     = VK_MULTIPLY;
02560     const unsigned int keyPADDIV     = VK_DIVIDE;
02561 
02562 #else
02563     // Define unknow keycodes when no display are available.
02564     // (should rarely be used then !).
02565     const unsigned int keyESC        = 1U;
02566     const unsigned int keyF1         = 2U;
02567     const unsigned int keyF2         = 3U;
02568     const unsigned int keyF3         = 4U;
02569     const unsigned int keyF4         = 5U;
02570     const unsigned int keyF5         = 6U;
02571     const unsigned int keyF6         = 7U;
02572     const unsigned int keyF7         = 8U;
02573     const unsigned int keyF8         = 9U;
02574     const unsigned int keyF9         = 10U;
02575     const unsigned int keyF10        = 11U;
02576     const unsigned int keyF11        = 12U;
02577     const unsigned int keyF12        = 13U;
02578     const unsigned int keyPAUSE      = 14U;
02579     const unsigned int key1          = 15U;
02580     const unsigned int key2          = 16U;
02581     const unsigned int key3          = 17U;
02582     const unsigned int key4          = 18U;
02583     const unsigned int key5          = 19U;
02584     const unsigned int key6          = 20U;
02585     const unsigned int key7          = 21U;
02586     const unsigned int key8          = 22U;
02587     const unsigned int key9          = 23U;
02588     const unsigned int key0          = 24U;
02589     const unsigned int keyBACKSPACE  = 25U;
02590     const unsigned int keyINSERT     = 26U;
02591     const unsigned int keyHOME       = 27U;
02592     const unsigned int keyPAGEUP     = 28U;
02593     const unsigned int keyTAB        = 29U;
02594     const unsigned int keyQ          = 30U;
02595     const unsigned int keyW          = 31U;
02596     const unsigned int keyE          = 32U;
02597     const unsigned int keyR          = 33U;
02598     const unsigned int keyT          = 34U;
02599     const unsigned int keyY          = 35U;
02600     const unsigned int keyU          = 36U;
02601     const unsigned int keyI          = 37U;
02602     const unsigned int keyO          = 38U;
02603     const unsigned int keyP          = 39U;
02604     const unsigned int keyDELETE     = 40U;
02605     const unsigned int keyEND        = 41U;
02606     const unsigned int keyPAGEDOWN   = 42U;
02607     const unsigned int keyCAPSLOCK   = 43U;
02608     const unsigned int keyA          = 44U;
02609     const unsigned int keyS          = 45U;
02610     const unsigned int keyD          = 46U;
02611     const unsigned int keyF          = 47U;
02612     const unsigned int keyG          = 48U;
02613     const unsigned int keyH          = 49U;
02614     const unsigned int keyJ          = 50U;
02615     const unsigned int keyK          = 51U;
02616     const unsigned int keyL          = 52U;
02617     const unsigned int keyENTER      = 53U;
02618     const unsigned int keySHIFTLEFT  = 54U;
02619     const unsigned int keyZ          = 55U;
02620     const unsigned int keyX          = 56U;
02621     const unsigned int keyC          = 57U;
02622     const unsigned int keyV          = 58U;
02623     const unsigned int keyB          = 59U;
02624     const unsigned int keyN          = 60U;
02625     const unsigned int keyM          = 61U;
02626     const unsigned int keySHIFTRIGHT = 62U;
02627     const unsigned int keyARROWUP    = 63U;
02628     const unsigned int keyCTRLLEFT   = 64U;
02629     const unsigned int keyAPPLEFT    = 65U;
02630     const unsigned int keyALT        = 66U;
02631     const unsigned int keySPACE      = 67U;
02632     const unsigned int keyALTGR      = 68U;
02633     const unsigned int keyAPPRIGHT   = 69U;
02634     const unsigned int keyMENU       = 70U;
02635     const unsigned int keyCTRLRIGHT  = 71U;
02636     const unsigned int keyARROWLEFT  = 72U;
02637     const unsigned int keyARROWDOWN  = 73U;
02638     const unsigned int keyARROWRIGHT = 74U;
02639     const unsigned int keyPAD0       = 75U;
02640     const unsigned int keyPAD1       = 76U;
02641     const unsigned int keyPAD2       = 77U;
02642     const unsigned int keyPAD3       = 78U;
02643     const unsigned int keyPAD4       = 79U;
02644     const unsigned int keyPAD5       = 80U;
02645     const unsigned int keyPAD6       = 81U;
02646     const unsigned int keyPAD7       = 82U;
02647     const unsigned int keyPAD8       = 83U;
02648     const unsigned int keyPAD9       = 84U;
02649     const unsigned int keyPADADD     = 85U;
02650     const unsigned int keyPADSUB     = 86U;
02651     const unsigned int keyPADMUL     = 87U;
02652     const unsigned int keyPADDIV     = 88U;
02653 #endif
02654 
02655     const double PI = 3.14159265358979323846;   //!< Definition of the mathematical constant PI
02656 
02657     // Definition of a 10x13 font (small size).
02658     const unsigned int font10x13[256*10*13/32] = {
02659       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02660       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80100c0,
02661       0x68000300,0x801,0xc00010,0x100c000,0x68100,0x100c0680,0x2,0x403000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02662       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02663       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x4020120,
02664       0x58120480,0x402,0x1205008,0x2012050,0x58080,0x20120581,0x40000001,0x804812,0x2000000,0x0,0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02665       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x140,0x80000,0x200402,0x800000,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02666       0x0,0x7010,0x7000000,0x8000200,0x20000,0xc0002000,0x8008,0x0,0x0,0x0,0x0,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02667       0x0,0x0,0x80000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x80100c0,0x68000480,0x1001,
02668       0xc00010,0x1018000,0x68100,0x100c0680,0x4,0x403000,0x1020000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,0x28081883,0x200801,
02669       0x2a00000,0x10,0x1c0201c0,0x70040f80,0xc0f81c07,0x0,0x70,0x3e0303c0,0x3c3c0f83,0xe03c2107,0xe08810,0x18c31070,0x3c0703c0,
02670       0x783e0842,0x22222208,0x83e04010,0x1008000,0x4000200,0x20001,0x2002,0x408008,0x0,0x0,0x100000,0x0,0x1008,0x2000000,0x0,0x0,0x0,
02671       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20080,0x38000880,0x8078140f,0x81c00000,0x3e000,0xc020180,0x60080001,0xe0000002,0xc00042,0x108e2010,
02672       0xc0300c0,0x300c0303,0xf83c3e0f,0x83e0f81c,0x701c070,0x3c0c41c0,0x701c0701,0xc0001d08,0x42108421,0x8820088,0x4020120,0x58140480,
02673       0x802,0x1205008,0x3014050,0xc058080,0x20120581,0x40000002,0x804814,0x2020050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,
02674       0x281e2484,0x80200801,0x1c02000,0x10,0x22060220,0x880c0801,0x82208,0x80000001,0x20008,0x41030220,0x40220802,0x402102,0x209010,
02675       0x18c31088,0x22088220,0x80080842,0x22222208,0x80204010,0x1014000,0x200,0x20001,0x2000,0x8008,0x0,0x0,0x100000,0x0,0x1008,
02676       0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40000500,0x80800010,0x40200000,0x41000,0x12020040,0x10000003,0xa0000006,
02677       0x12000c4,0x31014000,0xc0300c0,0x300c0302,0x80402008,0x2008008,0x2008020,0x220c4220,0x88220882,0x20002208,0x42108421,0x8820088,
02678       0x0,0x300,0x0,0x0,0x0,0x14000000,0x0,0x200200,0x0,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xfc282504,0x80001000,
02679       0x82a02000,0x20,0x22020020,0x8140802,0x102208,0x80801006,0x18008,0x9c848220,0x80210802,0x802102,0x20a010,0x15429104,0x22104220,
02680       0x80080842,0x22221405,0x404008,0x1022000,0x703c0,0x381e0701,0xc0783c02,0xc09008,0x1d83c070,0x3c078140,0x381c0882,0x21242208,
02681       0x81e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,0x40220500,0x80800027,0x20e02800,0x9c800,0x12020040,
02682       0x20000883,0xa0200002,0x120a044,0x11064010,0x12048120,0x48120484,0x80802008,0x2008008,0x2008020,0x210a4411,0x4411044,0x10884508,
02683       0x42108421,0x503c0b0,0x1c0701c0,0x701c0707,0x70381c07,0x1c07008,0x2008020,0x20f01c0,0x701c0701,0xc0201c08,0x82208822,0x883c088,
02684       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x50281903,0x20001000,0x80802000,0x20,0x22020040,0x30240f03,0xc0101c08,0x80801018,
02685       0x1fc06010,0xa48483c0,0x80210f03,0xe0803f02,0x20c010,0x15429104,0x22104220,0x70080841,0x41540805,0x804008,0x1041000,0x8220,
02686       0x40220881,0x882202,0x40a008,0x12422088,0x22088180,0x40100882,0x21241408,0x80201008,0x2031000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02687       0x0,0x20280,0x401c0200,0x700028,0x21205000,0x92800,0xc1fc080,0x10000883,0xa0200002,0x1205049,0x12c19010,0x12048120,0x48120484,
02688       0xf0803c0f,0x3c0f008,0x2008020,0x790a4411,0x4411044,0x10504908,0x42108421,0x5022088,0x2008020,0x8020080,0x88402208,0x82208808,
02689       0x2008020,0x1e088220,0x88220882,0x20002608,0x82208822,0x8822088,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x501c0264,
02690       0xa0001000,0x8001fc00,0x7000020,0x22020080,0x83e0082,0x20202207,0x80000020,0x1020,0xa4848220,0x80210802,0x9c2102,0x20c010,
02691       0x12425104,0x3c1043c0,0x8080841,0x41540802,0x804008,0x1000000,0x78220,0x40220f81,0x882202,0x40c008,0x12422088,0x22088100,
02692       0x60100881,0x41540805,0x406008,0x1849000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0xf0140200,0x880028,0x20e0a03f,0x709c800,
02693       0x201c0,0x60000881,0xa0000007,0xc0284b,0x122eb020,0x12048120,0x48120487,0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,
02694       0x10204908,0x42108421,0x2022088,0x1e0781e0,0x781e0787,0xf8403e0f,0x83e0f808,0x2008020,0x22088220,0x88220882,0x21fc2a08,0x82208822,
02695       0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xf80a0294,0x40001000,0x80002000,0x20,0x22020100,0x8040082,0x20202200,
02696       0x80000018,0x1fc06020,0xa48fc220,0x80210802,0x842102,0x20a010,0x12425104,0x20104240,0x8080841,0x41541402,0x1004008,0x1000000,
02697       0x88220,0x40220801,0x882202,0x40a008,0x12422088,0x22088100,0x18100881,0x41540805,0x801008,0x2046000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02698       0x0,0x0,0x0,0x20280,0x401c0f80,0x80880028,0x20005001,0x94800,0x20000,0x880,0xa0000000,0x5015,0x4215040,0x3f0fc3f0,0xfc3f0fc8,
02699       0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,0x10505108,0x42108421,0x203c088,0x22088220,0x88220888,0x80402008,0x2008008,
02700       0x2008020,0x22088220,0x88220882,0x20002a08,0x82208822,0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa00a0494,0x60001000,
02701       0x80002004,0x8020,0x22020200,0x88040882,0x20402201,0x801006,0x18000,0x9f084220,0x40220802,0x442102,0x209010,0x10423088,0x20088220,
02702       0x8080840,0x80882202,0x2004008,0x1000000,0x88220,0x40220881,0x882202,0x409008,0x12422088,0x22088100,0x8100880,0x80881402,
02703       0x1001008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0x40220200,0x80700027,0x20002801,0x92800,0x1fc000,0x980,
02704       0xa0000000,0xa017,0x84417840,0x21084210,0x84210848,0x80402008,0x2008008,0x2008020,0x2208c220,0x88220882,0x20882208,0x42108421,
02705       0x2020088,0x22088220,0x88220888,0xc8402208,0x82208808,0x2008020,0x22088220,0x88220882,0x20203208,0x82208822,0x2022020,0x0,0x0,0x0,
02706       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xa03c0463,0x90000801,0x2004,0x8040,0x1c0703e0,0x70040701,0xc0401c06,0x801001,0x20020,
02707       0x400843c0,0x3c3c0f82,0x3c2107,0x1c0881e,0x10423070,0x20070210,0xf0080780,0x80882202,0x3e04004,0x1000000,0x783c0,0x381e0701,
02708       0x782202,0x408808,0x12422070,0x3c078100,0x700c0780,0x80882202,0x1e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,
02709       0xf8000200,0x80080010,0x40000001,0x41000,0x0,0xe80,0xa0000000,0x21,0x8e21038,0x21084210,0x84210848,0xf83c3e0f,0x83e0f81c,
02710       0x701c070,0x3c08c1c0,0x701c0701,0xc0005c07,0x81e0781e,0x20200b0,0x1e0781e0,0x781e0787,0x30381c07,0x1c07008,0x2008020,0x1c0881c0,
02711       0x701c0701,0xc0201c07,0x81e0781e,0x203c020,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x801,0x4,0x40,0x0,0x0,0x0,0x1000,
02712       0x0,0x3c000000,0x0,0x0,0x0,0x0,0x10000,0x0,0x0,0x4004,0x1000000,0x0,0x0,0x80000,0x400000,0x0,0x20008000,0x0,0x4,0x1008,0x2000000,
02713       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x8008000f,0x80000000,0x3e000,0x0,0x800,0xa0000400,0x0,0x0,0x0,0x0,0x80000,0x0,
02714       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100000,0x0,0x0,0x0,0x0,0x2000,0x0,0x4020040,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,
02715       0x402,0x8,0x40,0x0,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x7004,0x70000fc,0x0,0x0,0x700000,0x800000,0x0,0x20008000,
02716       0x0,0x4,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x80f00000,0x0,0x0,0x0,0x800,0xa0001800,0x0,0x0,0x0,0x0,
02717       0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x4020040
02718     };
02719 
02720     // Definition of a 12x24 font (normal size).
02721     const unsigned int font12x24[12*24*256/32] = {
02722       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02723       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x80000000,0x198000,0x0,0x0,0x0,0x0,
02724       0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc001806,0xc81980,0x60000000,0xc001806,0x1980c00,0x18060198,0xc80c,
02725       0x180600,0xc8198000,0xc001,0x80601980,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02726       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02727       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,
02728       0x0,0x0,0x0,0x0,0x0,0x0,0x600300f,0x1301980,0x90000000,0x600300f,0x1980600,0x300f0198,0x13006,0x300f01,0x30198000,0x6003,
02729       0xf01980,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02730       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02731       0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x60000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7007,0x3c0000,0x3006019,
02732       0x80000000,0x90000000,0x3006019,0x80000300,0x60198000,0x3,0x601980,0x0,0x3006,0x1980000,0x60000000,0x0,0x0,0xe0000000,0x0,
02733       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02734       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000000,
02735       0x0,0x0,0x0,0x0,0x0,0xc800019,0x80000000,0x198000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x1001,0x420000,0x0,0x0,0x90000000,
02736       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000c06,0xc80001,0x10000000,0x18000c06,0x1800,0xc060000,0xc818,0xc0600,0xc8000000,
02737       0x18000,0xc0600000,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80660207,0x800f8060,0x300c004,0x0,0x6,
02738       0xe00703f,0x3f00383,0xf80f07fc,0x1f01f000,0x0,0xf8,0x607f,0x7c7e07,0xfe7fe0f8,0x6063fc1f,0x86066007,0xe7060f0,0x7f80f07f,
02739       0x81f8fff6,0x6606c03,0x70ee077f,0xe0786000,0xf0070000,0xc000060,0xc0,0x3e000,0x60006003,0x600fc00,0x0,0x0,0x0,0x0,0x0,0x3c0603,
02740       0xc0000000,0x7800000,0xf0000,0x0,0xf00001f,0x80001fe0,0x7fe000,0x0,0x0,0x0,0x168fe609,0x0,0x90e07,0x6000,0x3c000e,0x70000f8,
02741       0x1980001f,0x0,0x1f8,0xf00000f,0xf00180,0xfe000,0xe00e,0x1001,0x20060,0x6006006,0x600600,0x600fe07c,0x7fe7fe7f,0xe7fe3fc3,
02742       0xfc3fc3fc,0x7e07060f,0xf00f00,0xf00f0000,0xf360660,0x6606606e,0x76001e0,0xc00180f,0x1681981,0x10000000,0xc00180f,0x1980c00,
02743       0x180f0198,0x3801680c,0x180f01,0x68198000,0xc001,0x80f01980,0x18600198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,
02744       0x8044020c,0xc01f8060,0x2004004,0x0,0xc,0x3f81f07f,0x87f80383,0xf81f87fc,0x3f83f800,0x0,0x1fc,0x780607f,0x81fe7f87,0xfe7fe1fc,
02745       0x6063fc1f,0x860c6007,0xe7061f8,0x7fc1f87f,0xc3fcfff6,0x6606c03,0x30c6067f,0xe0783000,0xf00d8000,0x6000060,0xc0,0x7e000,0x60006003,
02746       0x600fc00,0x0,0x0,0xc00,0x0,0x0,0x7c0603,0xe0000000,0xfc00000,0x1f0000,0x0,0x900003f,0xc0003fe0,0x7fe000,0x0,0x0,0x0,0x1302660f,
02747       0x0,0xf0606,0x6004,0x7e0006,0x60601f8,0x19800001,0x80000000,0x1f8,0x19800010,0x81080300,0x3f2000,0x2011,0x1001,0x1c0060,0x6006006,
02748       0x600600,0x601fe1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f87061f,0x81f81f81,0xf81f8000,0x3fa60660,0x66066066,0x66003f0,0x6003009,
02749       0x1301981,0x10000000,0x6003009,0x1980600,0x30090198,0x1f013006,0x300901,0x30198000,0x6003,0x901980,0x30600198,0x0,0x0,0x0,
02750       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc0f8c,0xc0180060,0x6006044,0x40000000,0xc,0x3181b041,0xc41c0783,0x388018,
02751       0x71c71800,0x0,0x106,0x18c0f061,0xc38261c6,0x600384,0x60606001,0x86186007,0xe78630c,0x60e30c60,0xe7040606,0x630cc03,0x39c30c00,
02752       0xc0603000,0x3018c000,0x3000060,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,0x60000000,0x18400000,0x180000,
02753       0x0,0x19800070,0x40003600,0xc000,0x0,0x0,0x0,0x25a06,0x0,0x6030c,0x4,0xe20007,0xe060180,0xf000,0x80000000,0xf0000,0x10800000,
02754       0x80080600,0x7f2000,0x2020,0x80001001,0x20000,0xf00f00f,0xf00f00,0x601b0382,0x60060060,0x6000600,0x60060060,0x61c78630,0xc30c30c3,
02755       0xc30c000,0x30e60660,0x66066063,0xc600738,0x3006019,0x80000000,0xe0000000,0x3006019,0x80000300,0x60198000,0x3e000003,0x601980,
02756       0x0,0x3006,0x1980000,0x60600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc1fcc,0xc0180060,0x6006035,0x80000000,
02757       0x18,0x71c03000,0xc00c0583,0x300018,0x60c60c00,0x0,0x6,0x3060f060,0xc30060c6,0x600300,0x60606001,0x86306007,0x9e78670e,0x60670e60,
02758       0x66000606,0x630c606,0x19830c01,0xc0601800,0x30306000,0x60,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,
02759       0x60000000,0x18000000,0x300000,0x0,0x78060,0x6600,0x1c000,0x300c,0x39819c0,0x0,0x25a00,0x0,0x30c,0x4,0xc00003,0xc060180,0x30c1f,
02760       0x80000000,0x30c000,0x10800001,0x80700000,0x7f2000,0x2020,0x80001001,0x20060,0xf00f00f,0xf00f00,0xf01b0300,0x60060060,0x6000600,
02761       0x60060060,0x60c78670,0xe70e70e7,0xe70e000,0x70c60660,0x66066063,0xc7f8618,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0,
02762       0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x87ff3a4c,0xc0180060,0x400600e,0x600000,0x18,0x60c03000,
02763       0xc00c0d83,0x700018,0x60c60c00,0x20,0x400006,0x3060f060,0xc6006066,0x600600,0x60606001,0x86606006,0x966c6606,0x60660660,0x66000606,
02764       0x630c666,0xf019801,0x80601800,0x30603000,0x1f06f,0xf01ec0,0xf03fe1ec,0x6703e01f,0x61c0c06,0xdc6701f0,0x6f01ec0c,0xe1f87fc6,
02765       0xc60cc03,0x71c60c7f,0xc0600600,0x60000000,0x30000000,0x300000,0x40040,0x88060,0x6600,0x18000,0x300c,0x1981980,0x0,0x2421f,
02766       0x80003ce0,0x7fc198,0x601f,0xc02021,0x980600c0,0x40230,0x80000000,0x402000,0x19806003,0x80006,0xc7f2000,0x2020,0x80001001,
02767       0x420060,0xf00f00f,0xf00f00,0xf01b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x6606208,0x60e60660,0x66066061,
02768       0x987fc670,0x1f01f01f,0x1f01f01,0xf039c0f0,0xf00f00f,0xf03e03,0xe03e03e0,0x1f06701f,0x1f01f01,0xf01f0060,0x1e660c60,0xc60c60c6,
02769       0xc6f060c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x7ff3207,0x8c0c0000,0xc00300e,0x600000,0x30,0x60c03000,
02770       0xc01c0983,0xf0600030,0x31860c06,0x6001e0,0x78000e,0x23e1f861,0xc6006066,0x600600,0x60606001,0x86c06006,0x966c6606,0x60660660,
02771       0xe7000606,0x630c666,0xf01f803,0x600c00,0x30000000,0x3f87f,0x83f83fc3,0xf83fe3fc,0x7f83e01f,0x6380c07,0xfe7f83f8,0x7f83fc0d,
02772       0xf3fc7fc6,0xc71cc03,0x3183187f,0xc0600600,0x60000000,0xff806000,0x300000,0x40040,0x88070,0x6600,0x60030060,0x6001818,0x1883180,
02773       0x0,0x2423f,0xc0007ff0,0x607fc1f8,0x603f,0x80c01fc1,0xf80601e0,0x5f220,0x80420000,0x5f2000,0xf006006,0x80006,0xc7f2000,0x2020,
02774       0x82107c07,0xc03c0060,0x1f81f81f,0x81f81f80,0xf03b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x660671c,0x61660660,
02775       0x66066061,0xf860e6c0,0x3f83f83f,0x83f83f83,0xf87fe3f8,0x3f83f83f,0x83f83e03,0xe03e03e0,0x3f87f83f,0x83f83f83,0xf83f8060,
02776       0x3fc60c60,0xc60c60c3,0x187f8318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x883200,0x300c0000,0xc003035,0x80600000,
02777       0x30,0x66c03001,0xc0f81983,0xf86f0030,0x1f071c06,0x600787,0xfe1e001c,0x6261987f,0x86006067,0xfe7fc600,0x7fe06001,0x87c06006,
02778       0xf6646606,0x60e6067f,0xc3e00606,0x61986f6,0x600f007,0x600c00,0x30000000,0x21c71,0x830831c3,0x1c06031c,0x71c06003,0x6700c06,
02779       0x6671c318,0x71831c0f,0x16040c06,0xc318606,0x1b031803,0x80600600,0x60000000,0x30009000,0x300000,0x40040,0x7003e,0x67e0,0x90070090,
02780       0x9001818,0x8c3100,0x0,0x60,0x4000e730,0x900380f0,0x6034,0x80c018c7,0xfe060338,0xb0121,0x80c60000,0x909000,0x6008,0x1080006,
02781       0xc3f2000,0x2011,0x3180060,0x60060e0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0x60664660,0x66066066,
02782       0x66063b8,0x62660660,0x66066060,0xf06066c0,0x21c21c21,0xc21c21c2,0x1c466308,0x31c31c31,0xc31c0600,0x60060060,0x31871c31,0x83183183,
02783       0x18318000,0x71860c60,0xc60c60c3,0x18718318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1981a00,0xe03e0000,0xc003044,
02784       0x40600000,0x60,0x66c03001,0x80f03182,0x1c7f8030,0x3f83fc06,0x601e07,0xfe078038,0x6661987f,0x86006067,0xfe7fc61e,0x7fe06001,
02785       0x87e06006,0x66666606,0x7fc6067f,0x81f80606,0x61986f6,0x6006006,0x600600,0x30000000,0xc60,0xc60060c6,0xc06060c,0x60c06003,
02786       0x6e00c06,0x6660c60c,0x60c60c0e,0x6000c06,0xc318666,0x1f031803,0x600600,0x603c2000,0x30016800,0x1fe0000,0x1f81f8,0x1c1f,0x804067e1,
02787       0x68060168,0x16800810,0xc42300,0x0,0x60,0x20c331,0x68030060,0x6064,0x3fc1040,0xf006031c,0xa011e,0x818c7fe0,0x909000,0x7fe1f,
02788       0x80f00006,0xc0f2060,0xf80e,0x18c0780,0x780781c0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0xfc666660,
02789       0x66066066,0x66061f0,0x66660660,0x66066060,0x606066e0,0xc00c00,0xc00c00c0,0xc066600,0x60c60c60,0xc60c0600,0x60060060,0x60c60c60,
02790       0xc60c60c6,0xc60c000,0x61c60c60,0xc60c60c3,0x1860c318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1980f81,0x80373000,
02791       0xc003004,0x7fe0001,0xf0000060,0x60c03003,0x183180,0xc71c060,0x3181ec00,0x7000,0xe070,0x66619860,0xc6006066,0x60061e,0x60606001,
02792       0x87606006,0x66626606,0x7f860661,0xc01c0606,0x6198696,0xf00600e,0x600600,0x30000000,0x1fc60,0xc60060c7,0xfc06060c,0x60c06003,
02793       0x7c00c06,0x6660c60c,0x60c60c0c,0x7f00c06,0xc3b8666,0xe01b007,0x3c00600,0x3c7fe000,0xff03ec00,0x1fe0000,0x40040,0xe001,0xc0806603,
02794       0xec0e03ec,0x3ec00010,0x0,0x60000000,0x7f,0x10c3f3,0xec070060,0x6064,0x3fc1040,0x6000030c,0xa0100,0x3187fe1,0xf09f1000,0x7fe00,
02795       0x6,0xc012060,0x0,0xc63c03,0xc03c0380,0x19819819,0x81981981,0x98330600,0x60060060,0x6000600,0x60060060,0xfc662660,0x66066066,
02796       0x66060e0,0x6c660660,0x66066060,0x6060e630,0x1fc1fc1f,0xc1fc1fc1,0xfc3fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6,
02797       0xc60c7fe,0x62c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe02c6,0x3c633000,0xc003004,
02798       0x7fe0001,0xf00000c0,0x60c03006,0xc6180,0xc60c060,0x60c00c00,0x7000,0xe060,0x66639c60,0x66006066,0x600606,0x60606001,0x86306006,
02799       0x66636606,0x60060660,0xc0060606,0x61f8696,0xf00600c,0x600300,0x30000000,0x3fc60,0xc60060c7,0xfc06060c,0x60c06003,0x7c00c06,
02800       0x6660c60c,0x60c60c0c,0x1f80c06,0xc1b0666,0xe01b00e,0x3c00600,0x3c43c000,0x3007de00,0x600000,0x40040,0x30000,0x61006607,0xde0c07de,
02801       0x7de00000,0x0,0xf07fefff,0x1f,0x8008c3f7,0xde0e0060,0x6064,0xc01047,0xfe00018c,0xb013f,0x86300061,0xf0911000,0x6000,0x6,
02802       0xc012060,0x3f,0x8063c0cc,0x3cc0c700,0x39c39c39,0xc39c39c1,0x98630600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,
02803       0x66061f0,0x78660660,0x66066060,0x607fc618,0x3fc3fc3f,0xc3fc3fc3,0xfc7fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6,
02804       0xc60c7fe,0x64c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe0260,0x6661b000,0xc003000,
02805       0x600000,0xc0,0x60c0300c,0xc7fe0,0xc60c060,0x60c01c00,0x1e07,0xfe078060,0x6663fc60,0x66006066,0x600606,0x60606001,0x86386006,
02806       0x6636606,0x60060660,0xe0060606,0x60f039c,0x1b806018,0x600300,0x30000000,0x70c60,0xc60060c6,0x6060c,0x60c06003,0x7600c06,
02807       0x6660c60c,0x60c60c0c,0x1c0c06,0xc1b03fc,0xe01f01c,0xe00600,0x70000000,0x3007fc00,0x600000,0x40040,0x0,0x62006607,0xfc1807fc,
02808       0x7fc00000,0x0,0xf0000000,0x1,0xc004c307,0xfc1c0060,0x6064,0xc018c0,0x600000d8,0x5f200,0x3180060,0x50a000,0x6000,0x6,0xc012000,
02809       0x0,0xc601c0,0x4201c600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,0x66063b8,
02810       0x70660660,0x66066060,0x607f860c,0x70c70c70,0xc70c70c7,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,
02811       0x68c60c60,0xc60c60c1,0xf060c1f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3300260,0x6661e000,0xc003000,0x600000,
02812       0x180,0x71c03018,0xc7fe0,0xc60c0c0,0x60c01800,0x787,0xfe1e0060,0x6663fc60,0x630060c6,0x600306,0x60606001,0x86186006,0x661e70e,
02813       0x60070c60,0x60060606,0x60f039c,0x19806038,0x600180,0x30000000,0x60c60,0xc60060c6,0x6060c,0x60c06003,0x6700c06,0x6660c60c,
02814       0x60c60c0c,0xc0c06,0xc1b039c,0x1f00e018,0x600600,0x60000000,0x1803f800,0x600000,0x40040,0x39e00,0x63006603,0xf83803f8,0x3f800000,
02815       0x0,0x60000000,0x0,0xc00cc303,0xf8180060,0x6064,0xc01fc0,0x60060070,0x40200,0x18c0060,0x402000,0x6000,0x6,0xc012000,0x0,0x18c0140,
02816       0x2014600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0300,0x60060060,0x6000600,0x60060060,0x60c61e70,0xe70e70e7,0xe70e71c,0x60e60660,0x66066060,
02817       0x6060060c,0x60c60c60,0xc60c60c6,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,0x70c60c60,0xc60c60c0,
02818       0xe060c0e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33022e0,0x6670c000,0xc003000,0x600600,0x60180,0x31803030,
02819       0x41c0184,0x1831c0c0,0x71c23806,0x6001e0,0x780000,0x62630c60,0xe38261c6,0x600386,0x60606043,0x860c6006,0x661e30c,0x60030c60,
02820       0x740e0607,0xe0f039c,0x31c06030,0x600180,0x30000000,0x61c71,0x830831c3,0x406031c,0x60c06003,0x6300c06,0x6660c318,0x71831c0c,
02821       0x41c0c07,0x1c0e039c,0x1b00e030,0x600600,0x60000000,0x1c41b00e,0x601cc0,0x401f8,0x45240,0xe1803601,0xb03001b0,0x1b000000,
02822       0x0,0x0,0x41,0xc008e711,0xb0300060,0x6034,0x80c02020,0x60060030,0x30c00,0xc60000,0x30c000,0x0,0x7,0x1c012000,0x0,0x3180240,
02823       0x6024608,0x30c30c30,0xc30c30c3,0xc630382,0x60060060,0x6000600,0x60060060,0x61c61e30,0xc30c30c3,0xc30c208,0x70c70e70,0xe70e70e0,
02824       0x6060068c,0x61c61c61,0xc61c61c6,0x1cc62308,0x30430430,0x43040600,0x60060060,0x31860c31,0x83183183,0x18318060,0x31c71c71,
02825       0xc71c71c0,0xe07180e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2203fc0,0x663f6000,0x6006000,0x600600,0x60300,
02826       0x3f81fe7f,0xc7f80187,0xf83f80c0,0x3f83f006,0x600020,0x400060,0x33e6067f,0xc1fe7f87,0xfe6001fe,0x6063fc7f,0x60e7fe6,0x660e3f8,
02827       0x6001f860,0x37fc0603,0xfc06030c,0x30c0607f,0xe06000c0,0x30000000,0x7fc7f,0x83f83fc3,0xfc0603fc,0x60c7fe03,0x61807c6,0x6660c3f8,
02828       0x7f83fc0c,0x7f80fc3,0xfc0e039c,0x3180607f,0xc0600600,0x60000000,0xfc0e00c,0x601986,0x66040040,0x4527f,0xc0803fe0,0xe07fe0e0,
02829       0xe000000,0x0,0x0,0x7f,0x80107ff0,0xe07fc060,0x603f,0x83fe0000,0x60060018,0xf000,0x420000,0xf0000,0x7fe00,0x7,0xfe012000,
02830       0x0,0x2100640,0xc0643f8,0x60660660,0x66066067,0xec3e1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f860e3f,0x83f83f83,0xf83f8000,
02831       0x5fc3fc3f,0xc3fc3fc0,0x606006fc,0x7fc7fc7f,0xc7fc7fc7,0xfcffe3f8,0x3fc3fc3f,0xc3fc7fe7,0xfe7fe7fe,0x3f860c3f,0x83f83f83,
02832       0xf83f8060,0x7f83fc3f,0xc3fc3fc0,0x607f8060,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2201f80,0x3c1e7000,0x6006000,
02833       0x600,0x60300,0xe01fe7f,0xc3f00183,0xe01f0180,0x1f01e006,0x600000,0x60,0x3006067f,0x807c7e07,0xfe6000f8,0x6063fc3e,0x6067fe6,
02834       0x660e0f0,0x6000f060,0x3bf80601,0xf806030c,0x60e0607f,0xe06000c0,0x30000000,0x1ec6f,0xf01ec0,0xf80601ec,0x60c7fe03,0x61c03c6,
02835       0x6660c1f0,0x6f01ec0c,0x3f007c1,0xcc0e030c,0x71c0c07f,0xc0600600,0x60000000,0x7804018,0xe01186,0x66040040,0x39e3f,0x80401fe0,
02836       0x407fe040,0x4000000,0x0,0x0,0x3f,0x203ce0,0x407fc060,0x601f,0x3fe0000,0x60060018,0x0,0x0,0x0,0x7fe00,0x6,0xe6012000,0x0,
02837       0x7e0,0x1807e1f0,0x60660660,0x66066066,0x6c3e07c,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7e060e0f,0xf00f00,0xf00f0000,0x8f01f81f,
02838       0x81f81f80,0x60600670,0x1ec1ec1e,0xc1ec1ec1,0xec79c0f0,0xf80f80f,0x80f87fe7,0xfe7fe7fe,0x1f060c1f,0x1f01f01,0xf01f0000,0x4f01cc1c,
02839       0xc1cc1cc0,0xc06f00c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x6006000,0x600,0x600,0x0,0x0,0x0,0x0,
02840       0x600000,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x600060,0x30000000,0x0,0x0,0xc,0x3,0x0,0x0,0x60000c00,0x0,
02841       0x0,0xc000,0x600600,0x60000000,0x18,0xc03100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f8,0x0,0x0,0x0,0x0,0x6,
02842       0x12000,0x2000000,0x40,0x20004000,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02843       0x0,0xc06000c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x2004000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,
02844       0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0xc00,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x21c,0x3,0x0,0x0,0x60000c00,0x0,0x0,0xc000,
02845       0x7c0603,0xe0000000,0x10,0xc02300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f0,0x0,0x0,0x0,0x0,0x6,0x12000,0x1000000,
02846       0x40,0x7e004000,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc06000c0,0x0,
02847       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x300c000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,0x0,0x7800000,0x0,
02848       0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x3f8,0x3e,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x3c0603,0xc0000000,
02849       0x10,0xfc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x60000,0x0,0x0,0x0,0x0,0x6,0x0,0x1000000,0x0,0x0,0x0,0x0,
02850       0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02851       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,
02852       0x0,0x1f0,0x3c,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x600,0x0,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02853       0x0,0x0,0x0,0x0,0x0,0x6,0x0,0xe000000,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,
02854       0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02855       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02856       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02857       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
02858 
02859     // Definition of a 16x32 font (large size).
02860     const unsigned int font16x32[16*32*256/32] = {
02861       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02862       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02863       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02864       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70000e0,0x3c00730,0xe7001c0,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0x730,0x70000e0,0x3c00730,
02865       0xe700000,0x700,0xe003c0,0xe7000e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02866       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02867       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02868       0x0,0x0,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02869       0x0,0x0,0x18001c0,0x6600ff0,0xe7003e0,0x0,0x18001c0,0x6600e70,0x18001c0,0x6600e70,0xff0,0x18001c0,0x6600ff0,0xe700000,0x180,
02870       0x1c00660,0xe7001c0,0x0,0x0,0x0,0x380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02871       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02872       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,
02873       0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00380,
02874       0xc300ce0,0xe700630,0x0,0x1c00380,0xc300e70,0x1c00380,0xc300e70,0xce0,0x1c00380,0xc300ce0,0xe700000,0x1c0,0x3800c30,0xe700380,
02875       0x0,0x0,0x0,0x7c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02876       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02877       0x0,0x0,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,
02878       0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x700000,0x0,0x0,0x0,0x7c007c00,0x3e000000,
02879       0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000070,0x1800000,0xc60,0x0,0xe000070,0x1800000,0xe000070,
02880       0x1800000,0x0,0xe000070,0x1800000,0x0,0xe00,0x700180,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02881       0x0,0x0,0x0,0x800000,0x0,0x600600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02882       0x0,0x0,0x3f0,0xfc0,0x0,0x7000000,0x38000000,0x1c0000,0xfc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,
02883       0x1801f00,0x0,0x0,0x1c,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7300000,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0xe700000,
02884       0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0x0,0xc000c00,0x43800000,0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02885       0xf80,0x70000e0,0x3c00730,0xe700c60,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0xe000730,0x70000e0,0x3c00730,0xe700000,0x700,
02886       0xe003c0,0xe7000e0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300000,0x803c00,0x7c00180,
02887       0xc00300,0x1000000,0x0,0x1c,0x3c007c0,0xfc007e0,0xe01ff8,0x3f03ffc,0x7e007c0,0x0,0x0,0x7c0,0x1c0,0x7f8003f0,0x7f007ff8,0x7ff803f0,
02888       0x70381ffc,0xff0700e,0x7000783c,0x783807c0,0x7fc007c0,0x7fc00fc0,0x7fff7038,0x700ee007,0x780f780f,0x7ffc03f0,0x70000fc0,0x3c00000,
02889       0x3000000,0x38000000,0x1c0000,0x1fc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1801f80,0x0,0x1f80000,
02890       0x7e,0x0,0x0,0x2400000,0xfc00000,0x7ff0000,0x7ffc0000,0x0,0x0,0x0,0x0,0xf30fb0c,0x2400000,0x0,0x240780f,0x1c0,0xfc,0x780f,
02891       0x18003f0,0xe700000,0x7c00000,0x0,0xff0,0x3c00000,0x78007c0,0xc00000,0xff80000,0xf80,0x7c00000,0xc000c00,0x18001c0,0x1c001c0,
02892       0x1c001c0,0x1c003e0,0x7fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007838,0x7c007c0,0x7c007c0,0x7c00000,0x7c67038,
02893       0x70387038,0x7038780f,0x70001fe0,0x30000c0,0x2400f30,0xe700c60,0x0,0x30000c0,0x2400e70,0x30000c0,0x2400e70,0xf700f30,0x30000c0,
02894       0x2400f30,0xe700000,0x300,0xc00240,0xe7000c0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,
02895       0x630018c,0x807e00,0xfe00180,0xc00300,0x1000000,0x0,0x38,0xff01fc0,0x3ff01ff0,0x1e01ff8,0x7f83ffc,0x1ff80ff0,0x0,0x0,0xff0,
02896       0x1f003e0,0x7fe00ff8,0x7fc07ff8,0x7ff80ff8,0x70381ffc,0xff0701c,0x7000783c,0x78381ff0,0x7fe01ff0,0x7fe01ff0,0x7fff7038,0x781ee007,
02897       0x3c1e380e,0x7ffc0380,0x380001c0,0x3c00000,0x1800000,0x38000000,0x1c0000,0x3c00000,0x380001c0,0xe01c00,0x3800000,0x0,0x0,
02898       0x0,0x7000000,0x0,0x0,0x1e0,0x18003c0,0x0,0x3fc0000,0x70,0x0,0x0,0x6600000,0x1ff00000,0x1fff0000,0x7ffc0000,0x0,0x0,0x0,0x0,
02899       0xcf0239c,0x3c00000,0x0,0x3c0380e,0x1c0,0x2001fe,0x380e,0x18007f8,0xe700000,0x8600000,0x0,0xff0,0x7e00000,0x8c00870,0x1800000,
02900       0x1ff80000,0x180,0xc600000,0xc000c00,0x38001c0,0x3e003e0,0x3e003e0,0x3e001c0,0x7fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,
02901       0x7fc07838,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x1fec7038,0x70387038,0x7038380e,0x70003ce0,0x1800180,0x6600cf0,0xe7007c0,0x0,
02902       0x1800180,0x6600e70,0x1800180,0x6600e70,0x7c00cf0,0x1800180,0x6600cf0,0xe700000,0x180,0x1800660,0xe700180,0x38000e70,0x0,
02903       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630030c,0x3f0e700,0x1e200180,0x1800180,0x21100000,0x0,
02904       0x38,0x1e7819c0,0x38781038,0x1e01c00,0xf080038,0x1c381c38,0x0,0x0,0x1878,0x7fc03e0,0x70e01e18,0x70e07000,0x70001e18,0x703801c0,
02905       0x707038,0x70007c7c,0x7c381c70,0x70701c70,0x70703830,0x1c07038,0x381ce007,0x1c1c3c1e,0x3c0380,0x380001c0,0x7e00000,0xc00000,
02906       0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,0x70c0000,0xe0,
02907       0x0,0x0,0xc300000,0x38300000,0x3c700000,0x3c0000,0x0,0x0,0x0,0x0,0xce022f4,0x1800000,0x0,0x1803c1e,0x1c0,0x2003c2,0x3c1e,
02908       0x1800e08,0x7e0,0x300000,0x0,0x7e00000,0xe700000,0x600030,0x3000000,0x3f980000,0x180,0x18200000,0xc000c00,0x1e0001c0,0x3e003e0,
02909       0x3e003e0,0x3e003e0,0xfe01e18,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70e07c38,0x1c701c70,0x1c701c70,0x1c700000,0x3c787038,
02910       0x70387038,0x70383c1e,0x70003870,0xc00300,0xc300ce0,0x380,0x0,0xc00300,0xc300000,0xc00300,0xc300000,0xfc00ce0,0xc00300,0xc300ce0,
02911       0x0,0xc0,0x3000c30,0x300,0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630031c,0xff8c300,
02912       0x1c000180,0x1800180,0x39380000,0x0,0x70,0x1c3801c0,0x203c001c,0x3e01c00,0x1c000038,0x381c3838,0x0,0x0,0x1038,0xe0e03e0,0x70703c08,
02913       0x70707000,0x70003808,0x703801c0,0x707070,0x70007c7c,0x7c383838,0x70383838,0x70387010,0x1c07038,0x381c700e,0x1e3c1c1c,0x780380,
02914       0x1c0001c0,0xe700000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,
02915       0x0,0xe000000,0xe0,0x0,0x1000100,0x3800,0x70100000,0x38700000,0x780000,0x1c0,0x7801ce0,0xe380000,0x0,0x2264,0x0,0x0,0x1c1c,
02916       0x0,0x200780,0x1c1c,0x1800c00,0x1818,0x7f00000,0x0,0x18180000,0xc300000,0x600070,0x0,0x7f980000,0x180,0x18300000,0xc000c00,
02917       0x3000000,0x3e003e0,0x3e003e0,0x3e003e0,0xee03c08,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,
02918       0x38380000,0x38387038,0x70387038,0x70381c1c,0x7fc03870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc00000,0x0,0x0,0x0,0x0,0x0,0x0,
02919       0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0xe88c300,0x1c000180,0x38001c0,
02920       0xfe00180,0x0,0x70,0x1c3801c0,0x1c001c,0x6e01c00,0x1c000078,0x381c3818,0x0,0x40000,0x40000038,0x1c0607e0,0x70703800,0x70707000,
02921       0x70003800,0x703801c0,0x7070e0,0x70007c7c,0x7c383838,0x70383838,0x70387000,0x1c07038,0x381c700e,0xf780e38,0x700380,0x1c0001c0,
02922       0x1c380000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,
02923       0xe000000,0xe0,0x0,0x1000100,0x4400,0x70000000,0x38700000,0x700000,0xe0,0x7001c70,0xe380000,0x0,0x2264,0x0,0x0,0xe38,0x0,
02924       0x200700,0xe38,0x1800c00,0x300c,0xc300000,0x0,0x300c0000,0xc300180,0x6003c0,0x0,0x7f980000,0x180,0x18300000,0xc000c00,0x1800000,
02925       0x7e007e0,0x7e007e0,0x7e003e0,0xee03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,0x38380000,
02926       0x38387038,0x70387038,0x70380e38,0x7ff039f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x40000,0x0,0x0,0x38000000,
02927       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0x1c80e700,0x1c000180,0x38001c0,0x3800180,
02928       0x0,0xe0,0x381c01c0,0x1c001c,0x6e01c00,0x38000070,0x381c381c,0x0,0x3c0000,0x78000078,0x38030770,0x70707800,0x70387000,0x70007000,
02929       0x703801c0,0x7071c0,0x7000745c,0x7638701c,0x7038701c,0x70387000,0x1c07038,0x1c38718e,0x7700f78,0xf00380,0xe0001c0,0x381c0000,
02930       0x7e0,0x39e003e0,0x79c03f0,0x3ffc079c,0x39e01fc0,0xfe01c1e,0x3807778,0x39e007e0,0x39e0079c,0x73c07e0,0x7ff83838,0x701ce007,
02931       0x783c701c,0x1ffc01c0,0x18001c0,0x0,0x1c000100,0xe0,0x0,0x1000100,0x4200,0x70000000,0x70700100,0xf00100,0x10000e0,0x7000c70,
02932       0xc700000,0x0,0x2204,0x7e00000,0x1e380100,0x1ffc0f78,0x0,0xf80700,0xf78,0x1800e00,0x63e6,0x18300000,0x0,0x6fe60000,0xe700180,
02933       0xc00060,0x3838,0x7f980000,0x180,0x18300000,0xc000c00,0x18001c0,0x7700770,0x7700770,0x77007f0,0xee07800,0x70007000,0x70007000,
02934       0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1008,0x707c7038,0x70387038,0x70380f78,0x707039c0,0x7e007e0,0x7e007e0,
02935       0x7e007e0,0x1f3c03e0,0x3f003f0,0x3f003f0,0x1fc01fc0,0x1fc01fc0,0x7f039e0,0x7e007e0,0x7e007e0,0x7e00380,0x7ce3838,0x38383838,
02936       0x3838701c,0x39e0701c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6307fff,0x1c807e0c,0xe000180,
02937       0x30000c0,0x3800180,0x0,0xe0,0x381c01c0,0x1c001c,0xce01fe0,0x38000070,0x381c381c,0x3800380,0xfc0000,0x7e0000f0,0x30030770,
02938       0x70707000,0x70387000,0x70007000,0x703801c0,0x707380,0x700076dc,0x7638701c,0x7038701c,0x70387800,0x1c07038,0x1c3873ce,0x7f00770,
02939       0xe00380,0xe0001c0,0x700e0000,0x1ff8,0x3ff00ff0,0xffc0ff8,0x3ffc0ffc,0x3bf01fc0,0xfe01c3c,0x3807f78,0x3bf00ff0,0x3ff00ffc,
02940       0x77e0ff0,0x7ff83838,0x3838e007,0x3c783838,0x1ffc01c0,0x18001c0,0x0,0x7ff00380,0x1e0,0x0,0x1000100,0x4200,0x78000000,0x70700380,
02941       0xe00380,0x3800060,0xe000e30,0x1c600000,0x0,0x2204,0xff00000,0x7f7c0380,0x1ffc0770,0x1c0,0x3fc0700,0x18040770,0x1800780,0x4e12,
02942       0x18300104,0x0,0x4c320000,0x7e00180,0x1c00030,0x3838,0x7f980000,0x180,0x18302080,0xc000c00,0x18001c0,0x7700770,0x7700770,
02943       0x7700770,0x1ee07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c381c,0x705c7038,0x70387038,
02944       0x70380770,0x70383b80,0x1ff81ff8,0x1ff81ff8,0x1ff81ff8,0x3fbe0ff0,0xff80ff8,0xff80ff8,0x1fc01fc0,0x1fc01fc0,0xff83bf0,0xff00ff0,
02945       0xff00ff0,0xff00380,0xffc3838,0x38383838,0x38383838,0x3ff03838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02946       0x0,0x1c0,0x7fff,0x1c803c38,0xf000000,0x70000e0,0xfe00180,0x0,0x1c0,0x381c01c0,0x3c0078,0xce01ff0,0x39e000f0,0x1c38381c,0x3800380,
02947       0x3e07ffc,0xf8001f0,0x307b0770,0x70e07000,0x70387000,0x70007000,0x703801c0,0x707700,0x700076dc,0x7638701c,0x7038701c,0x70387e00,
02948       0x1c07038,0x1c3873ce,0x3e007f0,0x1e00380,0x70001c0,0x0,0x1038,0x3c381e18,0x1c7c1e3c,0x3801e3c,0x3c7801c0,0xe01c78,0x380739c,
02949       0x3c781c38,0x3c381c3c,0x7c21e10,0x7003838,0x3838700e,0x1ef03838,0x3c01c0,0x18001c0,0x0,0x7fe007c0,0x1c0,0x0,0x1000100,0x6400,
02950       0x7e000000,0x707007c0,0x1e007c0,0x7c00070,0xe000638,0x18600000,0x0,0x0,0x1e100000,0x73ce07c0,0x3c07f0,0x1c0,0x7240700,0x1ddc3ffe,
02951       0x1800de0,0x8c01,0x1870030c,0x0,0x8c310000,0x3c00180,0x3800030,0x3838,0x7f980000,0x180,0x183030c0,0xc000c00,0x430001c0,0x7700770,
02952       0x7700770,0x7700770,0x1ce07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1c38,0x70dc7038,
02953       0x70387038,0x703807f0,0x70383b80,0x10381038,0x10381038,0x10381038,0x21e71e18,0x1e3c1e3c,0x1e3c1e3c,0x1c001c0,0x1c001c0,0x1e383c78,
02954       0x1c381c38,0x1c381c38,0x1c380380,0x1c383838,0x38383838,0x38383838,0x3c383838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
02955       0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0x1e8000e0,0x1f000000,0x70000e0,0x39380180,0x0,0x1c0,0x3b9c01c0,0x3c07f0,0x18e01078,0x3bf800e0,
02956       0x7e0383c,0x3800380,0x1f807ffc,0x3f001c0,0x61ff0e38,0x7fc07000,0x70387ff0,0x7ff07000,0x7ff801c0,0x707f00,0x7000729c,0x7338701c,
02957       0x7070701c,0x70703fc0,0x1c07038,0x1e7873ce,0x1c003e0,0x3c00380,0x70001c0,0x0,0x1c,0x3c381c00,0x1c3c1c1c,0x3801c3c,0x383801c0,
02958       0xe01cf0,0x380739c,0x38381c38,0x3c381c3c,0x7801c00,0x7003838,0x3838700e,0xfe03c78,0x7801c0,0x18001c0,0x0,0x1c000c20,0xff8,
02959       0x0,0x1ff01ff0,0x3818,0x3fc00100,0x707e0c20,0x3c00c20,0xc200030,0xc000618,0x18c00000,0x0,0x0,0x1c000080,0xe1ce0c20,0x7803e0,
02960       0x1c0,0xe200700,0xff83ffe,0x1801878,0x9801,0x1cf0071c,0x7ffc0000,0x8c310000,0x7ffe,0x7000030,0x3838,0x3f980380,0x180,0xc6038e0,
02961       0x7f9c7f9c,0x3e1c01c0,0xe380e38,0xe380e38,0xe380f78,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,0x1c001c0,0xfe387338,0x701c701c,
02962       0x701c701c,0x701c0e70,0x719c7038,0x70387038,0x703803e0,0x70383b80,0x1c001c,0x1c001c,0x1c001c,0xe71c00,0x1c1c1c1c,0x1c1c1c1c,
02963       0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380000,0x3c383838,0x38383838,0x38383c78,0x3c383c78,0x0,0x0,0x0,0x0,
02964       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0xf800380,0x3f830000,0x70000e0,0x31080180,0x0,0x380,0x3b9c01c0,
02965       0x7807e0,0x38e00038,0x3c3800e0,0xff01c3c,0x3800380,0x7c000000,0x7c03c0,0x61870e38,0x7fc07000,0x70387ff0,0x7ff070fc,0x7ff801c0,
02966       0x707f80,0x7000739c,0x7338701c,0x7ff0701c,0x7fe00ff0,0x1c07038,0xe7073ce,0x1c003e0,0x3800380,0x38001c0,0x0,0x1c,0x381c3800,
02967       0x381c380e,0x380381c,0x383801c0,0xe01de0,0x380739c,0x3838381c,0x381c381c,0x7001e00,0x7003838,0x1c70718e,0x7e01c70,0xf00380,
02968       0x18001e0,0x1e000000,0x1c001bb0,0xff8,0x0,0x1000100,0xe0,0xff00300,0x707e1bb0,0x3801bb0,0x1bb00010,0x8000308,0x30c00000,0x0,
02969       0x0,0x1e0000c0,0xe1ce1bb0,0xf003e0,0x1c0,0x1c203ff8,0x63003e0,0x180181c,0x9801,0xfb00e38,0x7ffc0000,0x8fc10000,0x7ffe,0xe000860,
02970       0x3838,0x1f980380,0x180,0x7c01c70,0x1f001f0,0x1f003c0,0xe380e38,0xe380e38,0xe380e38,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,
02971       0x1c001c0,0xfe387338,0x701c701c,0x701c701c,0x701c07e0,0x731c7038,0x70387038,0x703803e0,0x70383980,0x1c001c,0x1c001c,0x1c001c,
02972       0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x387c3838,0x38383838,0x38381c70,
02973       0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc30,0x7f00e00,0x33c30000,0x70000e0,0x1007ffe,
02974       0x0,0x380,0x3b9c01c0,0xf00078,0x30e0001c,0x3c1c01c0,0x1c381fdc,0x0,0x70000000,0x1c0380,0x63030e38,0x70707000,0x70387000,0x700070fc,
02975       0x703801c0,0x707b80,0x7000739c,0x7338701c,0x7fc0701c,0x7fc001f0,0x1c07038,0xe703e5c,0x3e001c0,0x7800380,0x38001c0,0x0,0x7fc,
02976       0x381c3800,0x381c380e,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7001fc0,0x7003838,0x1c70718e,0x7c01c70,
02977       0xe01f00,0x180007c,0x7f8c0000,0x7fc03fb8,0x1c0,0x0,0x1000100,0x700,0x1f00600,0x70703fb8,0x7803fb8,0x3fb80000,0x8000000,0x180,
02978       0x0,0x0,0x1fc00060,0xe1ce3fb8,0xe001c0,0x1c0,0x1c203ff8,0xc1801c0,0x180c,0x9801,0x1c70,0xc0000,0x8cc10000,0x180,0xfe007c0,
02979       0x3838,0x7980380,0xff0,0xe38,0x3e003e00,0x3e000380,0xe380e38,0xe380e38,0xe380e38,0x38e07000,0x70007000,0x70007000,0x1c001c0,
02980       0x1c001c0,0x70387338,0x701c701c,0x701c701c,0x701c03c0,0x731c7038,0x70387038,0x703801c0,0x703838e0,0x7fc07fc,0x7fc07fc,0x7fc07fc,
02981       0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x38dc3838,0x38383838,0x38381c70,
02982       0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc60,0xf83878,0x71e30000,0x70000e0,0x1007ffe,
02983       0x7f0,0x380,0x381c01c0,0x1e0003c,0x60e0001c,0x381c01c0,0x381c079c,0x0,0x7c000000,0x7c0380,0x63031c1c,0x70307000,0x70387000,
02984       0x7000701c,0x703801c0,0x7071c0,0x7000739c,0x71b8701c,0x7000701c,0x71e00078,0x1c07038,0xe703e7c,0x7e001c0,0xf000380,0x38001c0,
02985       0x0,0x1ffc,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fc0,0x380739c,0x3838381c,0x381c381c,0x7000ff0,0x7003838,0x1ef03bdc,
02986       0x3800ee0,0x1e01f00,0x180007c,0x61fc0000,0x7fc07f3c,0x1c0,0x0,0x1000100,0x1800,0x780c00,0x70707f3c,0xf007f3c,0x7f3c0000,0x0,
02987       0x3c0,0x3ffcffff,0x0,0xff00030,0xe1fe7f3c,0x1e001c0,0x1c0,0x1c200700,0xc183ffe,0xe0c,0x9801,0x1ff038e0,0xc07f0,0x8c610000,
02988       0x180,0x0,0x3838,0x1980380,0x0,0x1ff0071c,0xe000e000,0xe0000f80,0x1c1c1c1c,0x1c1c1c1c,0x1c1c1e38,0x38e07000,0x70007000,0x70007000,
02989       0x1c001c0,0x1c001c0,0x703871b8,0x701c701c,0x701c701c,0x701c03c0,0x761c7038,0x70387038,0x703801c0,0x70703870,0x1ffc1ffc,0x1ffc1ffc,
02990       0x1ffc1ffc,0xfff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x389c3838,0x38383838,
02991       0x38380ee0,0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xfffc,0xbc60fc,0x70e30000,0x70000e0,
02992       0x180,0x7f0,0x700,0x381c01c0,0x3e0001c,0x7ffc001c,0x381c03c0,0x381c001c,0x0,0x1f807ffc,0x3f00380,0x63031ffc,0x70387000,0x70387000,
02993       0x7000701c,0x703801c0,0x7071e0,0x7000701c,0x71b8701c,0x7000701c,0x70f00038,0x1c07038,0x7e03e7c,0x77001c0,0xe000380,0x1c001c0,
02994       0x0,0x3c1c,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x70003f8,0x7003838,0xee03bdc,
02995       0x3c00ee0,0x3c00380,0x18000e0,0xf00000,0x1c007e7c,0x3c0,0x0,0x1000100,0x0,0x381800,0x70707e7c,0xe007e7c,0x7e7c0000,0x0,0x7c0,
02996       0x0,0x0,0x3f80018,0xe1fe7e7c,0x3c001c0,0x1c0,0x1c200700,0xc183ffe,0xf0c,0x8c01,0x38e0,0xc07f0,0x8c710000,0x180,0x0,0x3838,
02997       0x1980000,0x0,0x71c,0x7000f0,0x700f00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x3fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
02998       0x703871b8,0x701c701c,0x701c701c,0x701c07e0,0x7c1c7038,0x70387038,0x703801c0,0x7ff03838,0x3c1c3c1c,0x3c1c3c1c,0x3c1c3c1c,
02999       0x3fff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x391c3838,0x38383838,0x38380ee0,
03000       0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfffc,0x9c01ce,0x70f60000,0x70000e0,0x180,
03001       0x0,0x700,0x381c01c0,0x780001c,0x7ffc001c,0x381c0380,0x381c003c,0x0,0x3e07ffc,0xf800380,0x63031ffc,0x70387000,0x70387000,
03002       0x7000701c,0x703801c0,0x7070f0,0x7000701c,0x71b8701c,0x7000701c,0x70700038,0x1c07038,0x7e03e7c,0xf7801c0,0x1e000380,0x1c001c0,
03003       0x0,0x381c,0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7000078,0x7003838,0xee03a5c,
03004       0x7c00fe0,0x78001c0,0x18001c0,0x0,0x1c003ef8,0x380,0x0,0x1000100,0x810,0x383000,0x70703ef8,0x1e003ef8,0x3ef80000,0x0,0x7c0,
03005       0x0,0x0,0x78000c,0xe1c03ef8,0x78001c0,0x1c0,0x1c200700,0x63001c0,0x18003f8,0x4e12,0x1c70,0xc0000,0x4c320000,0x180,0x0,0x3838,
03006       0x1980000,0x0,0xe38,0x700118,0x701e00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x7fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
03007       0x703871b8,0x701c701c,0x701c701c,0x701c0e70,0x7c1c7038,0x70387038,0x703801c0,0x7fc0381c,0x381c381c,0x381c381c,0x381c381c,
03008       0x78e03800,0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x3b1c3838,0x38383838,0x38380fe0,
03009       0x381c0fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1860,0x9c0186,0x707e0000,0x30000c0,0x180,
03010       0x0,0xe00,0x183801c0,0xf00001c,0xe0001c,0x181c0380,0x381c0038,0x0,0xfc0000,0x7e000000,0x61873c1e,0x70383800,0x70707000,0x7000381c,
03011       0x703801c0,0x707070,0x7000701c,0x70f83838,0x70003838,0x70780038,0x1c07038,0x7e03c3c,0xe3801c0,0x1c000380,0xe001c0,0x0,0x381c,
03012       0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01ef0,0x380739c,0x3838381c,0x381c381c,0x7000038,0x7003838,0xfe03e7c,0xfe007c0,
03013       0x70001c0,0x18001c0,0x0,0xe001ff0,0x380,0x0,0x1000100,0x162c,0x381800,0x30701ff0,0x1c001ff0,0x1ff00000,0x0,0x3c0,0x0,0x0,
03014       0x380018,0xe1c01ff0,0x70001c0,0x1c0,0x1c200700,0xff801c0,0x18000f0,0x63e6,0xe38,0x0,0x6c3e0000,0x0,0x0,0x3838,0x1980000,0x0,
03015       0x1c70,0xf0000c,0xf01c00,0x3c1e3c1e,0x3c1e3c1e,0x3c1e3c1c,0x70e03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x707070f8,
03016       0x38383838,0x38383838,0x38381c38,0x38387038,0x70387038,0x703801c0,0x7000381c,0x381c381c,0x381c381c,0x381c381c,0x70e03800,
03017       0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0380,0x3e1c3838,0x38383838,0x383807c0,0x381c07c0,
03018       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18c0,0x9c0186,0x783c0000,0x38001c0,0x180,0x3800000,
03019       0x3800e00,0x1c3801c0,0x1e00003c,0xe00038,0x1c1c0780,0x381c0038,0x3800380,0x3c0000,0x78000000,0x61ff380e,0x70383808,0x70707000,
03020       0x7000381c,0x703801c0,0x40707078,0x7000701c,0x70f83838,0x70003838,0x70384038,0x1c07038,0x7e03c3c,0x1e3c01c0,0x3c000380,0xe001c0,
03021       0x0,0x383c,0x3c381c00,0x1c3c1c00,0x3801c3c,0x383801c0,0xe01c78,0x380739c,0x38381c38,0x3c381c3c,0x7000038,0x7003878,0x7c01e78,
03022       0x1ef007c0,0xf0001c0,0x18001c0,0x0,0xe000ee0,0x7800380,0xe380000,0x1001ff0,0x2242,0x40380c00,0x38700ee0,0x3c000ee0,0xee00000,
03023       0x0,0x0,0x0,0x0,0x380030,0xe1c00ee0,0xf0001c0,0x1c0,0xe200700,0xdd801c0,0x1800038,0x300c,0x71c,0x0,0x300c0000,0x0,0x0,0x3838,
03024       0x1980000,0x0,0x38e0,0xb0000c,0xb01c08,0x380e380e,0x380e380e,0x380e380e,0x70e03808,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
03025       0x707070f8,0x38383838,0x38383838,0x3838381c,0x38387038,0x70387038,0x703801c0,0x7000381c,0x383c383c,0x383c383c,0x383c383c,
03026       0x70e01c00,0x1c001c00,0x1c001c00,0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383878,0x38783878,0x387807c0,
03027       0x3c3807c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x18c0,0x10b801ce,0x3c3e0000,0x38001c0,0x180,
03028       0x3800000,0x3801c00,0x1e7801c0,0x3c002078,0xe02078,0x1c380700,0x1c3810f0,0x3800380,0x40000,0x40000380,0x307b380e,0x70701e18,
03029       0x70e07000,0x70001c1c,0x703801c0,0x60e0703c,0x7000701c,0x70f83c78,0x70003c70,0x703c70f0,0x1c03870,0x3c01c3c,0x3c1c01c0,0x78000380,
03030       0x7001c0,0x0,0x3c7c,0x3c381e18,0x1c7c1e0c,0x3801c3c,0x383801c0,0xe01c38,0x3c0739c,0x38381c38,0x3c381c3c,0x7001078,0x7803c78,
03031       0x7c01c38,0x1c780380,0x1e0001c0,0x18001c0,0x0,0x70c06c0,0x7000380,0xe300000,0x1000100,0x2142,0x70f00600,0x3c7006c0,0x780006c0,
03032       0x6c00000,0x0,0x0,0x0,0x0,0x10780060,0x73e206c0,0x1e0001c0,0x1c0,0x7240700,0x180c01c0,0x1800018,0x1818,0x30c,0x0,0x18180000,
03033       0x0,0x0,0x3c78,0x1980000,0x0,0x30c0,0x130000c,0x1301c18,0x380e380e,0x380e380e,0x380e380e,0x70e01e18,0x70007000,0x70007000,
03034       0x1c001c0,0x1c001c0,0x70e070f8,0x3c783c78,0x3c783c78,0x3c781008,0x7c783870,0x38703870,0x387001c0,0x70003a3c,0x3c7c3c7c,0x3c7c3c7c,
03035       0x3c7c3c7c,0x79f11e18,0x1e0c1e0c,0x1e0c1e0c,0x1c001c0,0x1c001c0,0x1c783838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383c78,0x3c783c78,
03036       0x3c780380,0x3c380380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x38c0,0x1ff800fc,0x1fee0000,
03037       0x1800180,0x180,0x3800000,0x3801c00,0xff01ffc,0x3ffc3ff0,0xe03ff0,0xff00700,0x1ff81fe0,0x3800380,0x0,0x380,0x3000780f,0x7ff00ff8,
03038       0x7fc07ff8,0x70000ffc,0x70381ffc,0x7fe0701c,0x7ff8701c,0x70781ff0,0x70001ff0,0x701c7ff0,0x1c01fe0,0x3c01c38,0x380e01c0,0x7ffc0380,
03039       0x7001c0,0x0,0x1fdc,0x3ff00ff0,0xffc0ffc,0x3800fdc,0x38383ffe,0xe01c3c,0x1fc739c,0x38380ff0,0x3ff00ffc,0x7001ff0,0x3f81fb8,
03040       0x7c01c38,0x3c3c0380,0x1ffc01c0,0x18001c0,0x0,0x3fc0380,0x7000380,0xc70718c,0x1000100,0x2244,0x7ff00200,0x1fff0380,0x7ffc0380,
03041       0x3800000,0x0,0x0,0x0,0x0,0x1ff000c0,0x7f7e0380,0x1ffc01c0,0x1c0,0x3fc3ffe,0x1c0,0x1800018,0x7e0,0x104,0x0,0x7e00000,0x7ffe,
03042       0x0,0x3fde,0x1980000,0x0,0x2080,0x3300018,0x3300ff0,0x780f780f,0x780f780f,0x780f780e,0xf0fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,
03043       0x1ffc1ffc,0x7fc07078,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x7ff01fe0,0x1fe01fe0,0x1fe001c0,0x70003bf8,0x1fdc1fdc,0x1fdc1fdc,
03044       0x1fdc1fdc,0x3fbf0ff0,0xffc0ffc,0xffc0ffc,0x3ffe3ffe,0x3ffe3ffe,0xff03838,0xff00ff0,0xff00ff0,0xff00000,0x3ff01fb8,0x1fb81fb8,
03045       0x1fb80380,0x3ff00380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x31c0,0x7e00078,0x7cf0000,0x1800180,
03046       0x0,0x3800000,0x3803800,0x3c01ffc,0x3ffc0fe0,0xe01fc0,0x3e00e00,0x7e00f80,0x3800380,0x0,0x380,0x18007007,0x7fc003f0,0x7f007ff8,
03047       0x700003f0,0x70381ffc,0x3f80701e,0x7ff8701c,0x707807c0,0x700007c0,0x701e1fc0,0x1c00fc0,0x3c01818,0x780f01c0,0x7ffc0380,0x3801c0,
03048       0x0,0xf9c,0x39e003e0,0x79c03f0,0x380079c,0x38383ffe,0xe01c1e,0x7c739c,0x383807e0,0x39e0079c,0x7000fc0,0x1f80f38,0x3801c38,
03049       0x781e0380,0x1ffc01c0,0x18001c0,0x0,0x1f80100,0xe000700,0x1c60718c,0x1000100,0x1e3c,0x1fc00100,0x7ff0100,0x7ffc0100,0x1000000,
03050       0x0,0x0,0x0,0x0,0xfc00080,0x3e3c0100,0x1ffc01c0,0x1c0,0xf83ffe,0x1c0,0x1800838,0x0,0x0,0x0,0x0,0x7ffe,0x0,0x3b9e,0x1980000,
03051       0x0,0x0,0x2300038,0x23003e0,0x70077007,0x70077007,0x70077007,0xe0fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007078,
03052       0x7c007c0,0x7c007c0,0x7c00000,0xc7c00fc0,0xfc00fc0,0xfc001c0,0x700039f0,0xf9c0f9c,0xf9c0f9c,0xf9c0f9c,0x1f1e03e0,0x3f003f0,
03053       0x3f003f0,0x3ffe3ffe,0x3ffe3ffe,0x7e03838,0x7e007e0,0x7e007e0,0x7e00000,0x63e00f38,0xf380f38,0xf380380,0x39e00380,0x0,0x0,
03054       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x3000000,0x3800,0x0,0x0,0x0,0x0,
03055       0x0,0x300,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,0x380,0x3801c0,0x0,0x0,0x0,0x0,0x1c,0x0,0xe00000,
03056       0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1c0,0x18001c0,0x0,0x0,0xe000700,0x18600000,0x1000100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03057       0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800ff0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0x1800000,0x0,0x6300070,0x6300000,0x0,
03058       0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000000,
03059       0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x7000000,
03060       0x7000,0x0,0x0,0x0,0x0,0x0,0x700,0x0,0x0,0xf040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x3f0,0x1c0fc0,0x0,0x0,
03061       0x0,0x0,0x1c,0x0,0xe00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1e0,0x18003c0,0x0,0x0,0xc000700,0x18c00000,0x1000000,0x0,
03062       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x18007e0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000,
03063       0x0,0x7f800e0,0x7f80000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,
03064       0x0,0x0,0x0,0x0,0x0,0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,
03065       0x0,0x600600,0x0,0x6000000,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,
03066       0x3f0,0xfc0,0x0,0x0,0x0,0x0,0x838,0x0,0x1e00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0xf00,0xfc,0x1801f80,0x0,0x0,0x8008e00,0x30c00000,
03067       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000,
03068       0x0,0x3001c0,0x300000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,
03069       0x0,0x0,0x0,0x0,0x0,0xf00,0x38000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,
03070       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03071       0x0,0x0,0xff0,0x0,0x1fc00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3e00,0x7c,0x1801f00,0x0,0x0,0x800fe00,0x0,0x0,0x0,0x0,0x0,0x0,
03072       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7c00000,0x0,0x3001fc,0x300000,
03073       0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03074       0x3e00,0x38003e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03075       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x7e0,0x0,0x1f000000,
03076       0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3c00,0x0,0x1800000,0x0,0x0,0x7800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03077       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03078       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00,0x38003c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03079       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03080       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,0x0,
03081       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03082       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03083       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03084       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03085       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03086       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03087       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03088       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03089       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03090       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03091       0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
03092 
03093     // Definition of a 29x57 font (extra large size).
03094     const unsigned int font29x57[29*57*256/32] = {
03095       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03096       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03097       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03098       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03099       0x0,0x781e00,0x0,0x0,0x7,0x81e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03100       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0000,0xf8000,0x7e00000,0x0,0x7,
03101       0xc0000000,0x0,0x7c00,0xf80,0x7e000,0x0,0x7c00000,0xf80000,0x7e000000,0x0,0x0,0x1f00,0x3e0,0x1f800,0x0,0x0,0x0,0x3,0xe0000000,
03102       0x7c00003f,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03103       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03104       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03105       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03106       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03107       0x0,0x0,0x0,0x0,0x0,0x0,0x3c3c00,0x0,0x0,0x3,0xc3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,
03108       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0000,
03109       0x1f0000,0x7e00000,0xf838001f,0xf80001f,0xf0000000,0x0,0x3e00,0x1f00,0x7e000,0x3e1f000,0x3e00000,0x1f00000,0x7e00003e,0x1f000000,
03110       0x3e0,0xe0000f80,0x7c0,0x1f800,0x3e0e00,0x7c3e000,0x0,0x1,0xf0000000,0xf800003f,0x1f0f,0x800001f0,0x0,0x0,0x0,0x0,0x0,0x0,
03111       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03112       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03113       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03114       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03115       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e7800,0x0,0x0,
03116       0x1,0xe7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03117       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x1e0000,0xff00001,0xfe38001f,0xf80003f,
03118       0xf8000000,0x0,0x1e00,0x1e00,0xff000,0x3e1f000,0x1e00000,0x1e00000,0xff00003e,0x1f000000,0x7f8,0xe0000780,0x780,0x3fc00,0x7f8e00,
03119       0x7c3e000,0x0,0x0,0xf0000000,0xf000007f,0x80001f0f,0x800001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03120       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03121       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03122       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03123       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03124       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef000,0x0,0x0,0x0,0xef000000,0x0,0x0,0x0,0x0,0x0,0x0,
03125       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03126       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000,0x3c0000,0x1e780003,0xfff8001f,0xf80003c,0x78000000,0x0,0xf00,0x3c00,0x1e7800,
03127       0x3e1f000,0xf00000,0x3c00001,0xe780003e,0x1f000000,0xfff,0xe00003c0,0xf00,0x79e00,0xfffe00,0x7c3e000,0x0,0x0,0x78000001,0xe00000f3,
03128       0xc0001f0f,0x800003c0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03129       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03130       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03131       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03132       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03133       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03134       0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03135       0x0,0x78000,0x780000,0x3c3c0003,0x8ff0001f,0xf800078,0x3c000000,0x0,0x780,0x7800,0x3c3c00,0x3e1f000,0x780000,0x7800003,0xc3c0003e,
03136       0x1f000000,0xe3f,0xc00001e0,0x1e00,0xf0f00,0xe3fc00,0x7c3e000,0x0,0x0,0x3c000003,0xc00001e1,0xe0001f0f,0x80000780,0x0,0x0,
03137       0x0,0x0,0x0,0x0,0x1f,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03138       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03139       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03140       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03141       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03142       0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,
03143       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc00,0x7e000,0xfe000,0x0,0x3c000,0xf00000,0x781e0003,
03144       0x83e0001f,0xf800070,0x1c000000,0x0,0x3c0,0xf000,0x781e00,0x3e1f000,0x3c0000,0xf000007,0x81e0003e,0x1f000000,0xe0f,0x800000f0,
03145       0x3c00,0x1e0780,0xe0f800,0x7c3e000,0x0,0x0,0x1e000007,0x800003c0,0xf0001f0f,0x80000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf8000000,
03146       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03147       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03148       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03149       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03150       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03151       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03152       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ff800,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03153       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03154       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03155       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03156       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03157       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03158       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03159       0x0,0x0,0x78,0xf000000,0x0,0x0,0x780f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0,
03160       0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ffc00,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03161       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x3e000,0x3e00000,0x0,0x78,0x3c000000,0x0,0x1f000,0x3e0,
03162       0x3e000,0x0,0x1f000000,0x3e0000,0x3e000000,0x0,0x0,0x7c00,0xf8,0xf800,0x0,0x0,0x0,0xf,0x80000000,0x1f00001f,0x0,0x3e,0x0,
03163       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03164       0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03165       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80000,
03166       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03167       0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x781c0000,0x38,0xe000000,0x0,0x0,0x380e0,0x0,
03168       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x39c00,0x1ce000,0x303e00,
03169       0x0,0x0,0x0,0x0,0x0,0x78,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,
03170       0x0,0x0,0xf80000,0x7c000,0x3e00000,0xf0380000,0x70,0x1c000000,0x0,0xf800,0x7c0,0x3e000,0x0,0xf800000,0x7c0000,0x3e000000,
03171       0x0,0x3c0,0xe0003e00,0x1f0,0xf800,0x3c0e00,0x0,0x0,0x7,0xc0000000,0x3e00001f,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03172       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0xff,0x0,
03173       0xf8,0xf8000,0x1c000,0x0,0x0,0x0,0x0,0x1f,0xc0000000,0x1ff8,0xff00,0x0,0x0,0x3fe000,0x0,0x1fc00001,0xfe000000,0x0,0x0,0x0,
03174       0x0,0x7f800,0x0,0x0,0x0,0xff00000,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf8000000,0xfe,0x0,0x7f80,0x0,0x0,0x0,0x0,0x0,
03175       0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x780000,0x1,0xe0000000,0x0,0x780000,0x3,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,
03176       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x3fc00,0x0,0x0,0x1fc000,0x0,0x0,0x0,0x1fc0,
03177       0x0,0xff000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe1c0000,0x1c,0x1c000000,0x0,0x0,0x1c1c0,0x0,0x0,0x0,0x0,0x1fe0000,
03178       0x0,0x0,0x1ff,0x1f0f8,0x0,0xff000,0x0,0x0,0x0,0x3f,0xff00000f,0x80000000,0xfe0,0x3f80,0xf00,0x0,0x0,0x0,0x1,0xf8000003,0xe0000000,
03179       0x1c00,0xe000,0xe00,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,
03180       0x7f0000,0x0,0x1fc07000,0x0,0x0,0x0,0x0,0x0,0x3f800,0x780000,0x78000,0x7f00001,0xfc38001f,0xf800070,0x1c000000,0x0,0x7800,
03181       0x780,0x7f000,0x3e1f000,0x7800000,0x780000,0x7f00003e,0x1f0003f0,0x7f0,0xe0001e00,0x1e0,0x1fc00,0x7f0e00,0x7c3e000,0x0,0x3,
03182       0xc0000000,0x3c00003f,0x80001f0f,0x80000078,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03183       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x1e078000,0x30000000,0x3ff,0xc00001e0,0xf0,
03184       0x78000,0x1c000,0x0,0x0,0x0,0x0,0x1e0007f,0xf000007e,0x1ffff,0x7ffe0,0x1f80,0x3ffff80,0xfff803,0xfffff800,0xfff80007,0xff800000,
03185       0x0,0x0,0x0,0x0,0x1ffe00,0x0,0xfe0003,0xfff80000,0x3ffe01ff,0xe00003ff,0xffe01fff,0xff0003ff,0xe01e0007,0x803ffff0,0xfff80,
03186       0x3c000fc0,0x7800001f,0x8003f07e,0x1e000f,0xfe0007ff,0xf00003ff,0x8007ffe0,0x1fff8,0x7fffffe,0xf0003c1,0xe000079e,0xf1f,0x1f3e0,
03187       0x1f01ff,0xfff8003f,0xf003c000,0x7fe0,0x3f00,0x0,0x3c0000,0x1,0xe0000000,0x0,0x780000,0xf,0xfe000000,0x78000,0x3c00,0xf000,
03188       0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xfc0000f0,0x3fe00,0x0,0x0,0xfff00,0x0,0x0,0x3fe000,
03189       0x0,0x0,0x0,0x1dc0,0x0,0x3fff00,0x0,0x3ffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff1c07ff,0x3c0f001e,0x3c000000,
03190       0x0,0x0,0x1e3c0,0xf80007c,0x0,0x780000,0x0,0xfff8000,0x3e00,0x1f00000,0x7ff,0xc001f0f8,0x0,0x3ffc00,0x0,0x0,0x0,0x3f,0xff00003f,
03191       0xe0000000,0x3ff8,0xffe0,0x1e00,0x0,0xfffc00,0x0,0x7,0xf800000f,0xf8000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,
03192       0x3f800001,0xfc00003f,0xf80000ff,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,
03193       0xfc00,0x3c001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x0,0x7ff8f0f0,0x3c0780,0x1e03c00,0xf01e000,0x783e0001,0xf01e0000,0xffe00,
03194       0x3c0000,0xf0000,0x7700001,0xfe38001f,0xf800070,0x1c000000,0x0,0x3c00,0xf00,0x77000,0x3e1f000,0x3c00000,0xf00000,0x7700003e,
03195       0x1f0000f8,0xc0007f8,0xe0000f00,0x3c0,0x1dc00,0x7f8e00,0x7c3e000,0x0,0x1,0xe0000000,0x7800003b,0x80001f0f,0x800000f0,0x1e0000,
03196       0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03197       0x0,0x0,0x780000,0x3c1e0000,0x1e070000,0x300001f0,0x7ff,0xc00001e0,0x1e0,0x7c000,0x1c000,0x0,0x0,0x0,0x0,0x3c000ff,0xf80007fe,
03198       0x3ffff,0x801ffff8,0x1f80,0x3ffff80,0x3fff803,0xfffff801,0xfffc000f,0xffc00000,0x0,0x0,0x0,0x0,0x7fff80,0x0,0xfe0003,0xffff0000,
03199       0xffff01ff,0xfc0003ff,0xffe01fff,0xff000fff,0xf01e0007,0x803ffff0,0xfff80,0x3c001f80,0x7800001f,0xc007f07e,0x1e001f,0xff0007ff,
03200       0xfc0007ff,0xc007fffc,0x3fffc,0x7fffffe,0xf0003c1,0xf0000f9e,0xf0f,0x8003e1e0,0x1e01ff,0xfff8003f,0xf001e000,0x7fe0,0x3f00,
03201       0x0,0x1e0000,0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,
03202       0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x1fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x3de0,0x0,0x7fff80,0x0,0xfffff80,
03203       0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe7bc07ff,0x3e1f000f,0x78000000,0x0,0x0,0xf780,0x7800078,0x0,0x780000,0x180000,
03204       0x1fff8000,0x1e00,0x1e0003c,0xfff,0xc001f0f8,0x0,0x7ffe00,0x0,0x0,0x0,0x3f,0xff00007f,0xf0000000,0x3ffc,0xfff0,0x3c00,0x0,
03205       0x7fffc00,0x0,0x7,0xf800003f,0xfe000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xe00001ff,
03206       0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000fc00,0x3c003ffe,0x1fff0,
03207       0xfff80,0x7ffc00,0x3ffe000,0x0,0xfffce0f0,0x3c0780,0x1e03c00,0xf01e000,0x781e0001,0xe01e0000,0x3fff00,0x1e0000,0x1e0000,0xf780003,
03208       0xcf78001f,0xf800078,0x3c000000,0x0,0x1e00,0x1e00,0xf7800,0x3e1f000,0x1e00000,0x1e00000,0xf780003e,0x1f0000fc,0x7c000f3d,
03209       0xe0000780,0x780,0x3de00,0xf3de00,0x7c3e000,0x0,0x0,0xf0000000,0xf000007b,0xc0001f0f,0x800001e0,0x1e0000,0x3e1f00,0x0,0x0,
03210       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,
03211       0x3c1e0000,0x1e0f0000,0x300007fc,0xfff,0xc00001e0,0x1e0,0x3c000,0x1c000,0x0,0x0,0x0,0x0,0x3c001ff,0xfc001ffe,0x3ffff,0xc01ffffc,
03212       0x3f80,0x3ffff80,0x7fff803,0xfffff803,0xfffe001f,0xffe00000,0x0,0x0,0x0,0x0,0xffff80,0x7f800,0xfe0003,0xffff8001,0xffff01ff,
03213       0xff0003ff,0xffe01fff,0xff001fff,0xf01e0007,0x803ffff0,0xfff80,0x3c003f00,0x7800001f,0xc007f07f,0x1e003f,0xff8007ff,0xff000fff,
03214       0xe007ffff,0x7fffc,0x7fffffe,0xf0003c0,0xf0000f1e,0xf07,0x8003c1f0,0x3e01ff,0xfff8003f,0xf001e000,0x7fe0,0x7f80,0x0,0xe0000,
03215       0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,
03216       0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x3fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x78f0,0x0,0xffff80,0x0,0x3fffff80,0x1f,
03217       0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc7f80070,0x3e1f0007,0x70000000,0x0,0x0,0x7700,0x7c000f8,0x0,0x780000,0x180000,
03218       0x3fff8000,0x1f00,0x3e0003c,0x1f03,0xc001f0f8,0x0,0x703f00,0x0,0x0,0x0,0x3f,0xff0000f0,0xf8000000,0x303e,0xc0f8,0x7800,0x0,
03219       0xffffc00,0x0,0x7,0x3800003e,0x3e000000,0x1c00,0xe000,0x3c00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00000f,0xe00001ff,
03220       0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000fe00,0x3c007fff,0x3fff8,
03221       0x1fffc0,0xfffe00,0x7fff000,0x1,0xffffc0f0,0x3c0780,0x1e03c00,0xf01e000,0x781f0003,0xe01e0000,0x3fff80,0xe0000,0x3c0000,0x1e3c0003,
03222       0x8ff0001f,0xf80003c,0x78000000,0x0,0xe00,0x3c00,0x1e3c00,0x3e1f000,0xe00000,0x3c00001,0xe3c0003e,0x1f00007f,0xf8000e3f,0xc0000380,
03223       0xf00,0x78f00,0xe3fc00,0x7c3e000,0x0,0x0,0x70000001,0xe00000f1,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,
03224       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0000,
03225       0x30000ffe,0xf80,0xc00001e0,0x3c0,0x1e000,0x101c040,0x0,0x0,0x0,0x0,0x78003f0,0x7e001ffe,0x3f807,0xe01f00fe,0x3f80,0x3ffff80,
03226       0x7e01803,0xfffff007,0xe03f003f,0x3f00000,0x0,0x0,0x0,0x0,0xfc0fc0,0x3ffe00,0xfe0003,0xffffc003,0xf81f01ff,0xff8003ff,0xffe01fff,
03227       0xff003f01,0xf01e0007,0x803ffff0,0xfff80,0x3c007e00,0x7800001f,0xc007f07f,0x1e007e,0xfc007ff,0xff801f83,0xf007ffff,0x800fc07c,
03228       0x7fffffe,0xf0003c0,0xf0000f0f,0x1e07,0xc007c0f8,0x7c01ff,0xfff8003c,0xf000,0x1e0,0xffc0,0x0,0xf0000,0x1,0xe0000000,0x0,0x780000,
03229       0x3e,0x0,0x78000,0x3c00,0xf000,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0x800000f0,0x1f80,
03230       0x0,0x0,0x7e0780,0x0,0x0,0x1f82000,0x0,0x0,0x0,0x7070,0x0,0x1f80f80,0x0,0x7fffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,
03231       0x0,0x1,0xc3f80070,0x3f3f0007,0xf0000000,0x0,0x0,0x7f00,0x3e001f0,0x0,0x780000,0x180000,0x7f018000,0xf80,0x7c0003c,0x3e00,
03232       0x4001f0f8,0xfe00,0x400f00,0x0,0x0,0x0,0x7f000000,0xe0,0x38000000,0x1e,0x38,0x7800,0x0,0x1ffe1c00,0x0,0x0,0x38000078,0xf000000,
03233       0x1c00,0xe000,0x7f800,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xf00001ff,0xffc03f81,0xf007ffff,0xc03ffffe,
03234       0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf800fe00,0x3c00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,
03235       0x3,0xf07fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x780f8007,0xc01e0000,0x7e0fc0,0xf0000,0x3c0000,0x1c1c0003,0x87f0001f,0xf80003f,
03236       0xf8000000,0x0,0xf00,0x3c00,0x1c1c00,0x3e1f000,0xf00000,0x3c00001,0xc1c0003e,0x1f00003f,0xc0000e1f,0xc00003c0,0xf00,0x70700,
03237       0xe1fc00,0x7c3e000,0x0,0x0,0x78000001,0xe00000e0,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03238       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0001,0xff801e0f,
03239       0x1f00,0x1e0,0x3c0,0x1e000,0x3c1c1e0,0x0,0x0,0x0,0x0,0x78007c0,0x1f001f9e,0x3c001,0xf010003e,0x7780,0x3c00000,0xf800000,0xf007,
03240       0xc01f007c,0x1f80000,0x0,0x0,0x0,0x0,0xe003e0,0x7fff00,0x1ef0003,0xc007e007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x301e0007,
03241       0x80007800,0x780,0x3c00fc00,0x7800001f,0xe00ff07f,0x1e00f8,0x3e00780,0x1fc03e00,0xf807801f,0xc01f001c,0xf000,0xf0003c0,0xf0000f0f,
03242       0x1e03,0xc00f8078,0x780000,0xf0003c,0xf000,0x1e0,0x1f3e0,0x0,0x78000,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,
03243       0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0xf0,0xf80,0x0,0x0,0xf80180,0x0,0x0,0x1e00000,
03244       0x0,0x0,0x0,0xe038,0x0,0x3e00380,0x0,0xfe0f0000,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc0f00070,0x3b370003,0xe0000000,
03245       0x0,0x0,0x3e00,0x1e001e0,0x0,0x780000,0x180000,0x7c000000,0x780,0x780003c,0x3c00,0x0,0x7ffc0,0x780,0x0,0x0,0x3,0xffe00000,
03246       0x1c0,0x3c000000,0xe,0x38,0xf000,0x0,0x3ffe1c00,0x0,0x0,0x38000078,0xf000000,0x1c00,0xe000,0x7f000,0xf000,0x3de000,0x1ef0000,
03247       0xf780000,0x7bc00003,0xde00001e,0xf00003e7,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
03248       0xe0001e03,0xfc00fe00,0x3c01f007,0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x7,0xc01f80f0,0x3c0780,0x1e03c00,0xf01e000,0x78078007,
03249       0x801e0000,0x7803c0,0x78000,0x780000,0x380e0003,0x81e00000,0x1f,0xf0000000,0x0,0x780,0x7800,0x380e00,0x0,0x780000,0x7800003,
03250       0x80e00000,0x1ff,0x80000e07,0x800001e0,0x1e00,0xe0380,0xe07800,0x0,0x0,0x0,0x3c000003,0xc00001c0,0x70000000,0x780,0x1e0000,
03251       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03252       0x780000,0x3c1e0000,0x3c0e0007,0xfff01c07,0x1e00,0x1e0,0x780,0xf000,0x3e1c3e0,0x0,0x0,0x0,0x0,0xf0007c0,0x1f00181e,0x20000,
03253       0xf000001f,0xf780,0x3c00000,0x1f000000,0x1f00f,0x800f8078,0xf80000,0x0,0x0,0x0,0x0,0x8003e0,0x1fc0f80,0x1ef0003,0xc001e007,
03254       0x800101e0,0x7e003c0,0x1e00,0x7800,0x101e0007,0x80007800,0x780,0x3c00f800,0x7800001e,0xe00ef07f,0x801e00f0,0x1e00780,0x7c03c00,
03255       0x78078007,0xc01e0004,0xf000,0xf0003c0,0x78001e0f,0x1e03,0xe00f807c,0xf80000,0x1f0003c,0x7800,0x1e0,0x3e1f0,0x0,0x3c000,0x1,
03256       0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,
03257       0x1e,0xf0,0x780,0x0,0x0,0x1f00080,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x1e03c,0x0,0x3c00080,0x0,0xf80f0000,0x0,0x1f0000,0x0,0x0,
03258       0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x3bf70003,0xe0000000,0x0,0x0,0x3e00,0x1f003e0,0x0,0x780000,0x180000,0x78000000,0x7c0,0xf80003c,
03259       0x3c00,0x0,0x1f01f0,0x780,0x0,0x0,0xf,0x80f80000,0x1c0,0x1c000000,0xe,0x38,0x1e000,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,0x7800000,
03260       0x1c00,0xe000,0x7fc00,0xf000,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x80007800,0x10078000,0x3c0000,
03261       0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00ff00,0x3c01e003,0xc00f001e,0x7800f0,0x3c00780,0x1e003c00,
03262       0x7,0x800f00f0,0x3c0780,0x1e03c00,0xf01e000,0x7807c00f,0x801e0000,0xf803c0,0x3c000,0xf00000,0x780f0000,0x0,0x7,0xc0000000,
03263       0x0,0x3c0,0xf000,0x780f00,0x0,0x3c0000,0xf000007,0x80f00000,0x7ff,0xc0000000,0xf0,0x3c00,0x1e03c0,0x0,0x0,0x0,0x0,0x1e000007,
03264       0x800003c0,0x78000000,0xf00,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03265       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c1e001f,0xfff03803,0x80001e00,0x1e0,0x780,0xf000,0xf9cf80,
03266       0x0,0x0,0x0,0x0,0xf000780,0xf00001e,0x0,0xf800000f,0xe780,0x3c00000,0x1e000000,0x1e00f,0x78078,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,
03267       0x3f003c0,0x1ef0003,0xc000f00f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,0x3c01f000,0x7800001e,0xe00ef07f,
03268       0x801e01f0,0x1e00780,0x3c07c00,0x78078003,0xc03e0000,0xf000,0xf0003c0,0x78001e0f,0x1e01,0xf01f003c,0xf00000,0x3e0003c,0x7800,
03269       0x1e0,0x7c0f8,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,
03270       0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x8,0x40,0x0,0x7e0000,0x7c00000,0x1,0xf00f0000,
03271       0x0,0x3e0000,0x0,0x3f,0xfc0,0xfc3f0,0xfc3f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,0xf003c0,0x0,0x0,0x180000,0xf8000000,
03272       0x3c0,0xf00003c,0x3c00,0x0,0x3c0078,0x7ff80,0x0,0x0,0x1e,0x3c0000,0x1c0,0x1c000000,0xe,0xf0,0x0,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,
03273       0x7800000,0x1c00,0xe000,0x3c00,0x0,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x8000f800,0x78000,0x3c0000,
03274       0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00ff00,0x3c03e003,0xc01f001e,0xf800f0,0x7c00780,0x3e003c00,
03275       0xf,0x800f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803c00f,0x1fffc0,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03276       0x0,0x0,0x307,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03277       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x781e003f,0xfff03803,
03278       0x80001e00,0x1e0,0xf80,0xf000,0x3dde00,0x0,0x0,0x0,0x0,0xf000f00,0x780001e,0x0,0x7800000f,0x1e780,0x3c00000,0x3e000000,0x3e00f,
03279       0x780f0,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,0x7c001e0,0x3ef8003,0xc000f00f,0x1e0,0xf003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,
03280       0x3c03e000,0x7800001e,0xf01ef07b,0xc01e01e0,0xf00780,0x3e07800,0x3c078003,0xe03c0000,0xf000,0xf0003c0,0x78001e0f,0x1e00,0xf01e003e,
03281       0x1f00000,0x3c0003c,0x7800,0x1e0,0x78078,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,
03282       0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,
03283       0xe70000,0x7800000,0x1,0xe00f0000,0x0,0x3c0000,0x0,0x3f,0xfc0,0xfc1f0,0x1f83f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,
03284       0xf807c0,0x0,0x0,0x180000,0xf0000000,0x3e0,0x1f00003c,0x3e00,0x0,0x70001c,0x3fff80,0x0,0x0,0x38,0xe0000,0x1c0,0x1c000078,
03285       0x1c,0x1fe0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x7df000,0x3ef8000,0x1f7c0000,0xfbe00007,
03286       0xdf00003c,0x780003c7,0x8000f000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f780,
03287       0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0xf80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803e01f,0x1ffff8,0xf001e0,
03288       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x0,0x0,0x1e0000,
03289       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03290       0x780000,0x3c1e0000,0x781e003e,0x30703803,0x80001e00,0x1e0,0xf00,0x7800,0xff800,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,
03291       0x0,0x7800000f,0x3c780,0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x2000000,0x800000,0x1e0,0x78000e0,0x3c78003,
03292       0xc000f01e,0x1e0,0xf803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x701cf07b,0xc01e01e0,0xf00780,0x1e07800,
03293       0x3c078001,0xe03c0000,0xf000,0xf0003c0,0x7c003e0f,0x1e00,0xf83e001e,0x1e00000,0x7c0003c,0x3c00,0x1e0,0xf807c,0x0,0x0,0x1fe0001,
03294       0xe1fc0000,0x7f00003,0xf8780007,0xf000003c,0x7f0,0x783f0,0x0,0x0,0x7800000,0x1e00000,0x3e0f8000,0xfc00007,0xf8000007,0xf00001fc,
03295       0xf,0xc0003fc0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,0x1818000,
03296       0x7800000,0x1,0xe00f0000,0x0,0x7c0000,0x0,0x1f,0x80001f80,0x7c1f8,0x1f83e0,0x0,0x0,0x0,0x70,0x38c70007,0xf8000000,0x7f03,
03297       0xf0000000,0x0,0x780780,0x0,0x0,0xfe0000,0xf0000000,0x1e0,0x1e00003c,0x3f00,0x0,0xe07f0e,0x7fff80,0x0,0x0,0x70,0x70000,0x1c0,
03298       0x1c000078,0x3c,0x1fc0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x78f000,0x3c78000,0x1e3c0000,
03299       0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,
03300       0xf80f780,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0x1f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801e01e,0x1ffffc,
03301       0xf007e0,0x3fc000,0x1fe0000,0xff00000,0x7f800003,0xfc00001f,0xe0000fc0,0xfc00007f,0xfe0,0x7f00,0x3f800,0x1fc000,0x0,0x0,0x0,
03302       0x1,0xf000001f,0x80000ff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x1f80000,0x1fc1e000,0x0,0x0,0x0,0x0,0x1e1fc0,0x0,0x0,0x0,0x0,
03303       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,
03304       0x781c007c,0x30003803,0x80001f00,0x1e0,0xf00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,0x0,0x7800000f,0x3c780,
03305       0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x1e000000,0xf00000,0x3e0,0xf0000e0,0x3c78003,0xc000f01e,0x1e0,0x7803c0,
03306       0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c0f8000,0x7800001e,0x701cf079,0xe01e01e0,0xf00780,0x1e07800,0x3c078001,0xe03c0000,
03307       0xf000,0xf0003c0,0x3c003c0f,0x3e00,0x787c001f,0x3e00000,0xf80003c,0x3c00,0x1e0,0x1f003e,0x0,0x0,0x1fffc001,0xe7ff0000,0x3ffe000f,
03308       0xfe78003f,0xfc001fff,0xfe001ffc,0xf0078ffc,0x1ffc00,0x7ff000,0x7800f80,0x1e0000f,0x7f1fc01e,0x3ff0001f,0xfe00079f,0xfc0007ff,
03309       0x3c003c7f,0xf001fff8,0x1fffff0,0x3c003c0,0xf0000f1e,0xf1f,0x7c1f0,0x1f00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3c00000,0x100000,
03310       0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7800000,0x1,0xe00f0000,0x1000000,0xf80000,0x40000002,0xf,0x80001f00,0x7e0f8,0x1f07c0,
03311       0x0,0x0,0x0,0x70,0x38c7003f,0xff000000,0xff8f,0xf8000100,0xffffe,0x7c0f80,0x0,0x0,0x3ffc000,0xf0000020,0x1001f0,0x3c00003c,
03312       0x1f80,0x0,0x1c3ffc7,0x7c0780,0x0,0x0,0xe3,0xff038000,0xe0,0x38000078,0x78,0x1ff0,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,
03313       0x7800000,0x1c00,0xe000,0xe00,0xf000,0x78f000,0x3c78000,0x1e3c0000,0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,
03314       0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,
03315       0x4000200f,0x3f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801f03e,0x1ffffe,0xf01fe0,0x3fff800,0x1fffc000,0xfffe0007,0xfff0003f,
03316       0xff8001ff,0xfc003ff3,0xfe0003ff,0xe0007ff8,0x3ffc0,0x1ffe00,0xfff000,0x3ff80001,0xffc0000f,0xfe00007f,0xf000003f,0xf8003c7f,
03317       0xe0003ffc,0x1ffe0,0xfff00,0x7ff800,0x3ffc000,0x1f80000,0xfff1c03c,0x3c01e0,0x1e00f00,0xf007800,0x781f0001,0xf01e7ff0,0x7c0007c,
03318       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,
03319       0x3c1e003f,0xfffff078,0x30003803,0x80000f00,0x1e0,0x1f00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x3c000f00,0x780001e,0x0,0x7800000f,
03320       0x78780,0x3c00000,0x3c000000,0x7c00f,0x780f0,0x3c0007,0xe000003f,0x0,0xfe000000,0xfe0000,0x3c0,0x1f000070,0x7c7c003,0xc000f01e,
03321       0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c1f0000,0x7800001e,0x783cf079,0xe01e03c0,0xf00780,0x1e0f000,0x3c078001,
03322       0xe03c0000,0xf000,0xf0003c0,0x3c003c07,0x81f03c00,0x7c7c000f,0x87c00000,0xf00003c,0x1e00,0x1e0,0x3e001f,0x0,0x0,0x3fffe001,
03323       0xefff8000,0x7fff001f,0xff78007f,0xfe001fff,0xfe003ffe,0xf0079ffe,0x1ffc00,0x7ff000,0x7801f00,0x1e0000f,0xffbfe01e,0x7ff8003f,
03324       0xff0007bf,0xfe000fff,0xbc003cff,0xf803fffc,0x1fffff0,0x3c003c0,0x78001e1e,0xf0f,0x800f80f0,0x1e00ff,0xffe0001e,0xf0,0x780,
03325       0x0,0x0,0x3c00000,0x380000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1008000,0x7800000,0x3,0xe00f0000,0x3800000,0xf00000,0xe0000007,
03326       0xf,0x80001f00,0x3e0f8,0x1e07c0,0x0,0x0,0x0,0x70,0x3807007f,0xff800000,0x1ffdf,0xfc000380,0xffffe,0x3e1f00,0x0,0x0,0xfffe000,
03327       0xf0000030,0x3800f8,0x7c00003c,0xfc0,0x0,0x18780c3,0xf00780,0x80100,0x0,0xc3,0xffc18000,0xf0,0x78000078,0xf0,0xf0,0x0,0x3c003c0,
03328       0xfffe1c00,0x0,0x0,0x380000f0,0x7800801,0x1c00,0xe000,0x1e00,0xf000,0xf8f800,0x7c7c000,0x3e3e0001,0xf1f0000f,0x8f80007c,0x7c000787,
03329       0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078001,0xe03c000f,
03330       0x1e00078,0xf0003c0,0x78001e00,0xe000701f,0x3fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x7800f87c,0x1e007f,0xf07e00,0x7fffc00,0x3fffe001,
03331       0xffff000f,0xfff8007f,0xffc003ff,0xfe007ff7,0xff0007ff,0xf000fffc,0x7ffe0,0x3fff00,0x1fff800,0x3ff80001,0xffc0000f,0xfe00007f,
03332       0xf00000ff,0xf8003cff,0xf0007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x1f80001,0xfffb803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,
03333       0xe01efff8,0x3c00078,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03334       0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e003f,0xfffff078,0x30001c07,0xf80,0x1e0,0x1e00,0x3c00,0xff800,0x1e0000,0x0,0x0,0x0,0x3c001e00,
03335       0x3c0001e,0x0,0x7800001e,0x70780,0x3c00000,0x78000000,0x78007,0x800f00f0,0x3e0007,0xe000003f,0x3,0xfe000000,0xff8000,0x7c0,
03336       0x1e000070,0x783c003,0xc001f01e,0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c3e0000,0x7800001e,0x3838f079,
03337       0xe01e03c0,0x780780,0x1e0f000,0x1e078001,0xe03c0000,0xf000,0xf0003c0,0x3c007c07,0x81f03c00,0x3ef80007,0x87800000,0x1f00003c,
03338       0x1e00,0x1e0,0x7c000f,0x80000000,0x0,0x3ffff001,0xffffc000,0xffff003f,0xff7800ff,0xff001fff,0xfe007ffe,0xf007bffe,0x1ffc00,
03339       0x7ff000,0x7803e00,0x1e0000f,0xffffe01e,0xfff8007f,0xff8007ff,0xff001fff,0xbc003dff,0xf807fffc,0x1fffff0,0x3c003c0,0x78001e0f,
03340       0x1e07,0xc01f00f0,0x1e00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7c00000,0x7c0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1018000,0x7800000,
03341       0x3,0xc00f0000,0x7c00000,0x1f00001,0xf000000f,0x80000007,0xc0003e00,0x1e07c,0x3e0780,0x0,0x0,0x0,0x70,0x380700ff,0xff800000,
03342       0x3ffff,0xfe0007c0,0xffffe,0x1e1e00,0x0,0x780000,0x1fffe000,0xf0000078,0x7c0078,0x7800003c,0xff0,0x0,0x38e0003,0x80f00780,
03343       0x180300,0x0,0x1c3,0x81e1c000,0x7f,0xf0000078,0x1e0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800c01,0x80001c00,
03344       0xe000,0x603e00,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x7800078,0x3c000f87,0x8001e000,0x78000,0x3c0000,0x1e00000,
03345       0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f01,0xf000f81e,
03346       0x7bc0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007878,0x1e001f,0xf0f800,0x7fffe00,0x3ffff001,0xffff800f,0xfffc007f,0xffe003ff,
03347       0xff007fff,0xff800fff,0xf001fffe,0xffff0,0x7fff80,0x3fffc00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00001ff,0xfc003dff,0xf000ffff,
03348       0x7fff8,0x3fffc0,0x1fffe00,0xffff000,0x1f80003,0xffff803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,0xe01ffffc,0x3c00078,0x0,
03349       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,
03350       0x3c1e003f,0xfffff078,0x30001e0f,0x300780,0x1e0,0x1e00,0x3c00,0x3dde00,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf800003e,
03351       0xf0780,0x3dfc000,0x783f8000,0xf8007,0xc01f00f0,0x3e0007,0xe000003f,0x1f,0xfc000000,0x7ff000,0xf80,0x3e007c70,0x783c003,0xc001e03c,
03352       0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,0x80007800,0x780,0x3c7c0000,0x7800001e,0x3878f078,0xf01e03c0,0x780780,0x1e0f000,0x1e078001,
03353       0xe03e0000,0xf000,0xf0003c0,0x1e007807,0x83f03c00,0x3ef00007,0xcf800000,0x3e00003c,0xf00,0x1e0,0xf80007,0xc0000000,0x0,0x3e01f801,
03354       0xfe07e001,0xf80f007e,0x7f801f8,0x1f801fff,0xfe00fc0f,0xf007f83f,0x1ffc00,0x7ff000,0x7807c00,0x1e0000f,0x87e1e01f,0xe0fc00fc,
03355       0xfc007f8,0x1f803f03,0xfc003df0,0x3807e03c,0x1fffff0,0x3c003c0,0x78003e0f,0x1e03,0xe03e00f8,0x3e00ff,0xffe0001e,0xf0,0x780,
03356       0x0,0x0,0x7800000,0xfe0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7c00000,0x3,0xc00f0000,0xfe00000,0x3e00003,0xf800001f,
03357       0xc0000007,0xc0003e00,0x1e03c,0x3c0f80,0x0,0x0,0x0,0x70,0x380700fc,0x7800000,0x7c1fe,0x3e000fe0,0xffffe,0x1f3e00,0x0,0x780000,
03358       0x3f98e000,0xf000003c,0xfcf8007c,0xf800003c,0x3ffc,0x0,0x31c0001,0x80f00f80,0x380700,0x0,0x183,0x80e0c000,0x3f,0xe0000078,
03359       0x3c0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x38000078,0xf000e01,0xc003ffe0,0x1fff00,0x7ffc00,0xf000,0xf07800,0x783c000,0x3c1e0001,
03360       0xe0f0000f,0x7800078,0x3c000f07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,
03361       0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf801f01e,0xf3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007cf8,
03362       0x1e000f,0x80f0f000,0x7c03f00,0x3e01f801,0xf00fc00f,0x807e007c,0x3f003e0,0x1f80707f,0x8f801f80,0xf003f03f,0x1f81f8,0xfc0fc0,
03363       0x7e07e00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00003ff,0xfc003fc1,0xf801f81f,0x800fc0fc,0x7e07e0,0x3f03f00,0x1f81f800,0x1f80007,
03364       0xe07f003c,0x3c01e0,0x1e00f00,0xf007800,0x780f8003,0xe01fe07e,0x3e000f8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03365       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3f,0xfffff078,0x30000ffe,0x1f007c0,0x0,0x1e00,
03366       0x3c00,0xf9cf80,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf00000fc,0x1e0780,0x3fff800,0x78ffe000,0xf0003,0xe03e00f0,
03367       0x3e0007,0xe000003f,0x7f,0xe01fffff,0xf00ffc00,0x1f80,0x3c01ff70,0x783c003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,
03368       0x80007800,0x780,0x3cfc0000,0x7800001e,0x3c78f078,0xf01e03c0,0x780780,0x3e0f000,0x1e078003,0xc01f0000,0xf000,0xf0003c0,0x1e007807,
03369       0x83f83c00,0x1ff00003,0xcf000000,0x3e00003c,0xf00,0x1e0,0x0,0x0,0x0,0x20007801,0xfc03e003,0xe003007c,0x3f803e0,0x7c0003c,
03370       0xf807,0xf007e00f,0x3c00,0xf000,0x780f800,0x1e0000f,0x87e1f01f,0x803c00f8,0x7c007f0,0xf803e01,0xfc003f80,0x80f8004,0x3c000,
03371       0x3c003c0,0x3c003c0f,0x1e03,0xe03e0078,0x3c0000,0x7c0001e,0xf0,0x780,0x0,0x0,0x3ffff800,0x1ff0000,0x0,0x7800000,0x0,0x18,
03372       0xc0,0x0,0x1818000,0x3e00000,0x3,0xc00f0000,0x1ff00000,0x3e00007,0xfc00003f,0xe0000003,0xc0003c00,0xf03c,0x3c0f00,0x0,0x0,
03373       0x0,0x70,0x380701f0,0x800000,0x780fc,0x1e001ff0,0x7c,0xf3c00,0x0,0x780000,0x7e182000,0xf000001f,0xfff00ffc,0xffc0003c,0x3cfe,
03374       0x0,0x31c0001,0x80f01f80,0x780f00,0x0,0x183,0x80e0c000,0xf,0x80000078,0x780,0x38,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x38000078,
03375       0xf000f01,0xe003ffe0,0x1fff00,0x7ff800,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x78000f8,0x3e000f07,0x8003c000,0x78000,
03376       0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,
03377       0x78000f00,0x7c03e01e,0x1e3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78003cf0,0x1e0007,0x80f1e000,0x4000f00,0x20007801,0x3c008,
03378       0x1e0040,0xf00200,0x780403f,0x7803e00,0x3007c00f,0x803e007c,0x1f003e0,0xf801f00,0x780000,0x3c00000,0x1e000000,0xf00007f0,
03379       0x3e003f00,0x7801f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e003c,0x3c01e0,0x1e00f00,0xf007800,0x78078003,
03380       0xc01fc03e,0x1e000f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03381       0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xf078007c,0x300007fc,0x7e00fe0,0x0,0x1e00,0x3c00,0x3e1c3e0,0x1e0000,0x0,0x0,0x0,0xf0001e00,
03382       0x3c0001e,0x1,0xf000fff8,0x1e0780,0x3fffe00,0x79fff000,0x1f0001,0xfffc00f0,0x7e0007,0xe000003f,0x3ff,0x801fffff,0xf003ff80,
03383       0x3f00,0x3c03fff0,0xf01e003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3df80000,0x7800001e,
03384       0x1c70f078,0x781e03c0,0x780780,0x3c0f000,0x1e078007,0xc01f8000,0xf000,0xf0003c0,0x1e007807,0x83f83c00,0xfe00003,0xff000000,
03385       0x7c00003c,0x780,0x1e0,0x0,0x0,0x0,0x7c01,0xf801f007,0xc00100f8,0x1f803c0,0x3c0003c,0x1f003,0xf007c00f,0x80003c00,0xf000,
03386       0x783f000,0x1e0000f,0x3c0f01f,0x3e01f0,0x3e007e0,0x7c07c00,0xfc003f00,0xf0000,0x3c000,0x3c003c0,0x3c003c0f,0x1e01,0xf07c007c,
03387       0x7c0000,0xfc0001e,0xf0,0x780,0x0,0x0,0x3ffff000,0x3838000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0xff0000,0x3f00000,0x3,0xc00fff00,
03388       0x38380000,0x7c0000e,0xe000070,0x70000001,0xe0003c00,0xf01e,0x780e00,0x0,0x0,0x0,0x0,0x1e0,0x0,0x780f8,0xf003838,0xfc,0xffc00,
03389       0x0,0x780000,0x7c180000,0xf000000f,0xffe00fff,0xffc0003c,0x783f,0x80000000,0x6380000,0xc0f83f80,0xf81f00,0x0,0x303,0x80e06000,
03390       0x0,0x78,0xf00,0x78,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x3800003c,0x3e000f81,0xf003ffe0,0x1fff00,0x1fc000,0xf000,0x1e03c00,
03391       0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e000f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,
03392       0x3c000001,0xe0001e00,0x3c0f0f0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3e07c01e,0x1e3c0f0,0x3c0780,0x1e03c00,
03393       0xf01e000,0x78003ff0,0x1e0007,0x80f1e000,0xf80,0x7c00,0x3e000,0x1f0000,0xf80000,0x7c0001e,0x3c07c00,0x10078007,0x803c003c,
03394       0x1e001e0,0xf000f00,0x780000,0x3c00000,0x1e000000,0xf00007c0,0x1e003e00,0x7c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,
03395       0xf,0x801f003c,0x3c01e0,0x1e00f00,0xf007800,0x7807c007,0xc01f801f,0x1f001f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03396       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xe078003c,0x300001f0,0x3f801ff0,0x0,
03397       0x3c00,0x1e00,0x3c1c1e0,0x1e0000,0x0,0x0,0x0,0xf0001e0f,0x3c0001e,0x3,0xe000fff0,0x3c0780,0x3ffff00,0x7bfff800,0x1e0000,0x7ff00078,
03398       0x7e0007,0xe000003f,0x1ffc,0x1fffff,0xf0007ff0,0x7e00,0x3c07c3f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,
03399       0x1fffff,0x80007800,0x780,0x3ffc0000,0x7800001e,0x1ef0f078,0x781e03c0,0x780780,0x7c0f000,0x1e07801f,0x800ff000,0xf000,0xf0003c0,
03400       0xf00f807,0x83b83c00,0xfc00001,0xfe000000,0xf800003c,0x780,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0xc00000f0,0xf80780,0x3c0003c,
03401       0x1e001,0xf007c007,0x80003c00,0xf000,0x787e000,0x1e0000f,0x3c0f01f,0x1e01e0,0x1e007c0,0x3c07800,0x7c003f00,0xf0000,0x3c000,
03402       0x3c003c0,0x3e007c07,0x80003c00,0xf8f8003c,0x780000,0xf80001e,0xf0,0x780,0x0,0x0,0x7ffff000,0x601c000,0x3,0xffff0000,0x0,
03403       0xfff,0xf8007fff,0xc0000000,0x7e003c,0x1fe0000,0xc0003,0xc00fff00,0x601c0000,0xf800018,0x70000c0,0x38000001,0xe0007800,0x701e,
03404       0x701e00,0x0,0x0,0x0,0x0,0x1e0,0x6,0x700f8,0xf00601c,0xf8,0x7f800,0x0,0x780000,0xf8180000,0xf000000f,0x87c00fff,0xffc0003c,
03405       0xf01f,0xc0000000,0x6380000,0xc07ff780,0x1f03e03,0xfffffe00,0x303,0x81c06000,0x0,0x1ffff,0xfe001e00,0x180f8,0x0,0x3c003c0,
03406       0x3ffe1c00,0x3f00000,0x0,0x3800003f,0xfe0007c0,0xf8000000,0x18000000,0xc0000006,0x1f000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,
03407       0x3c000f0,0x1e001f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f0f0,
03408       0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f0f801e,0x3c3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,
03409       0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07c00,0xf0007,0x8078003c,0x3c001e0,0x1e000f00,0x780000,0x3c00000,
03410       0x1e000000,0xf0000f80,0x1f003e00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0xf,0x3f003c,0x3c01e0,0x1e00f00,0xf007800,
03411       0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03412       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe078003f,0xb0000000,0xfc003cf0,0x0,0x3c00,0x1e00,0x101c040,0x1e0000,0x0,0x0,0x1,
03413       0xe0001e1f,0x83c0001e,0x7,0xe000fff0,0x3c0780,0x3c03f80,0x7fc0fc00,0x1e0000,0xfff80078,0xfe0007,0xe000003f,0x7fe0,0x1fffff,
03414       0xf0000ffc,0xfc00,0x780f81f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3ffc0000,
03415       0x7800001e,0x1ef0f078,0x3c1e03c0,0x780780,0x1fc0f000,0x1e07ffff,0x7ff00,0xf000,0xf0003c0,0xf00f007,0xc3b87c00,0x7c00001,0xfe000000,
03416       0xf800003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0x800000f0,0xf80780,0x1e0003c,0x1e001,0xf0078007,0x80003c00,0xf000,0x78fc000,
03417       0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,0x3c07800,0x7c003e00,0xf0000,0x3c000,0x3c003c0,0x1e007807,0x80003c00,0x7df0003c,0x780000,
03418       0x1f00001e,0xf0,0x780,0x0,0x0,0x7800000,0xe7ce000,0x3,0xffff0000,0x0,0xfff,0xf8007fff,0xc0000000,0x1f0,0xffe000,0x1c0003,
03419       0xc00fff00,0xe7ce0000,0xf800039,0xf38001cf,0x9c000000,0xe0007800,0x780e,0x701c00,0x0,0x0,0x0,0x0,0x1e0,0x7,0xf0078,0xf00e7ce,
03420       0x1f0,0x7f800,0x0,0x780000,0xf0180000,0xf000000e,0x1c0001f,0xe000003c,0xf007,0xe0000000,0x6380000,0xc03fe780,0x3e07c03,0xfffffe00,
03421       0x303,0xffc06000,0x0,0x1ffff,0xfe003ffe,0x1fff0,0x0,0x3c003c0,0x1ffe1c00,0x3f00000,0x7,0xffc0001f,0xfc0003e0,0x7c000001,0xfc00000f,
03422       0xe000007f,0x1e000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,
03423       0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,
03424       0x783c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07800,
03425       0xf0003,0xc078001e,0x3c000f0,0x1e000780,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,
03426       0x7800780,0x3c003c00,0xf,0x7f003c,0x3c01e0,0x1e00f00,0xf007800,0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03427       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe070001f,0xf8000007,
03428       0xf0007cf8,0x7800000,0x3c00,0x1e00,0x1c000,0x1e0000,0x0,0x0,0x1,0xe0001e1f,0x83c0001e,0xf,0xc000fff8,0x780780,0x2000f80,0x7f803e00,
03429       0x3e0003,0xfffe007c,0x1fe0000,0x0,0x3ff00,0x0,0x1ff,0x8001f000,0x780f00f0,0x1f00f003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,
03430       0xfe03c00f,0xf81fffff,0x80007800,0x780,0x3ffe0000,0x7800001e,0xee0f078,0x3c1e03c0,0x7807ff,0xff80f000,0x1e07fffe,0x3ffe0,
03431       0xf000,0xf0003c0,0xf00f003,0xc7bc7800,0xfc00000,0xfc000001,0xf000003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xe000f80f,0x800001e0,
03432       0xf80f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x79f8000,0x1e0000f,0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003e00,
03433       0xf0000,0x3c000,0x3c003c0,0x1e007807,0x81e03c00,0x7df0003e,0xf80000,0x3e00003e,0xf0,0x7c0,0xfc000,0x80000000,0x7800000,0x1e7cf000,
03434       0x3,0xffff0000,0x0,0x18,0xc0,0x0,0xf80,0x7ffc00,0x380003,0xc00fff01,0xe7cf0000,0x1f000079,0xf3c003cf,0x9e000000,0xe0007000,
03435       0x380e,0xe01c00,0x0,0x0,0x0,0x0,0x1e0,0x3,0x800f0078,0xf01e7cf,0x3e0,0x3f000,0x0,0x780000,0xf018001f,0xfff8001e,0x1e0000f,
03436       0xc000003c,0xf003,0xe0000000,0x6380000,0xc00fc780,0x7c0f803,0xfffffe00,0x303,0xfe006000,0x0,0x1ffff,0xfe003ffe,0x1ffe0,0x0,
03437       0x3c003c0,0xffe1c00,0x3f00000,0x7,0xffc00007,0xf00001f0,0x3e00001f,0xfc0000ff,0xe00007ff,0x3e000,0x3e01e00,0x1f00f000,0xf8078007,
03438       0xc03c003e,0x1e001e0,0xf001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,
03439       0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000fc0,
03440       0x1e0007,0x80f1f000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c0f800,0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,
03441       0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1e,0xf7803c,0x3c01e0,0x1e00f00,
03442       0xf007800,0x7803e00f,0x801e000f,0x80f803e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03443       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe0f0000f,0xff00001f,0x8000f87c,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,
03444       0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x1f,0x800000fe,0xf00780,0x7c0,0x7f001e00,0x3c0007,0xe03f003f,0x3fe0000,0x0,0x3fc00,0x0,
03445       0x7f,0x8001e000,0x781f00f0,0x1e00f003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3f9f0000,0x7800001e,
03446       0xfe0f078,0x3c1e03c0,0x7807ff,0xff00f000,0x1e07fff8,0xfff8,0xf000,0xf0003c0,0xf81f003,0xc7bc7800,0xfe00000,0x78000003,0xe000003c,
03447       0x1e0,0x1e0,0x0,0x0,0x0,0x1fffc01,0xe000780f,0x1e0,0x780f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7bf0000,0x1e0000f,
03448       0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xf8000,0x3c000,0x3c003c0,0x1f00f807,0x81f03c00,0x3fe0001e,0xf00000,0x7c00007c,
03449       0xf0,0x3e0,0x3ff801,0x80000000,0x7800000,0x3cfcf800,0x3,0xffff0000,0x0,0x18,0xc0,0x0,0x7c00,0x1fff00,0x700003,0xc00f0003,
03450       0xcfcf8000,0x3e0000f3,0xf3e0079f,0x9f000000,0xf000,0x1000,0x0,0x0,0x0,0x0,0x0,0x1f0,0x1,0xc00f0078,0xf03cfcf,0x800007c0,0x1e000,
03451       0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x8000003c,0xf001,0xf0000000,0x6380000,0xc0000000,0xf81f003,0xfffffe00,0x303,
03452       0x87006000,0x0,0x1ffff,0xfe003ffe,0x7f00,0x0,0x3c003c0,0x3fe1c00,0x3f00000,0x7,0xffc00000,0xf8,0x1f0001ff,0xf0000fff,0x80007ffc,
03453       0xfc000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf001e07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,
03454       0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3fc001e,0x1e03c0f0,0x3c0780,
03455       0x1e03c00,0xf01e000,0x78000780,0x1e0007,0x80f0fc00,0x3fff80,0x1fffc00,0xfffe000,0x7fff0003,0xfff8001f,0xffc0001e,0x3c0f000,
03456       0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,0x3c00000,0x1e000000,0xf0001e00,0xf803c00,0x3c078001,0xe03c000f,0x1e00078,
03457       0xf0003c0,0x78001e07,0xfffffe1e,0x1e7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801e00f,0x1e0007,0x807803c0,0x0,0x0,0x0,0x0,0x0,
03458       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00007,
03459       0xffc0007e,0xf03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x3f,0x3e,0xf00780,0x3c0,0x7e001e00,
03460       0x7c000f,0x800f001f,0xffde0000,0x0,0x3e000,0x0,0xf,0x8003e000,0x781e0070,0x1e00f003,0xc001f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,
03461       0xf81e0007,0x80007800,0x780,0x3f1f0000,0x7800001e,0x7c0f078,0x1e1e03c0,0x7807ff,0xfc00f000,0x1e07fffe,0xffc,0xf000,0xf0003c0,
03462       0x781e003,0xc71c7800,0x1ff00000,0x78000003,0xe000003c,0x1e0,0x1e0,0x0,0x0,0x0,0xffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,
03463       0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7f000,0x3c000,
03464       0x3c003c0,0xf00f007,0xc1f07c00,0x1fc0001f,0x1f00000,0xfc000ff8,0xf0,0x1ff,0xfffe07,0x80000000,0x7800000,0x7ffcfc00,0x0,0xf000000,
03465       0x0,0x18,0xc0,0x0,0x3e000,0x1ff80,0xe00003,0xc00f0007,0xffcfc000,0x3e0001ff,0xf3f00fff,0x9f800000,0x6000,0x0,0x0,0x7c000,
03466       0x0,0x0,0x0,0xfe,0x0,0xe00f007f,0xff07ffcf,0xc0000fc0,0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x80000000,0xf800,
03467       0xf0000000,0x6380000,0xc0000000,0x1f03c000,0x1e00,0x303,0x83806000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xfe1c00,0x3f00000,0x0,
03468       0x0,0x3c,0xf801fff,0xfff8,0x7ffc0,0x1f8000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf003c07,0x8003c000,0x78000,
03469       0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,
03470       0x78000f00,0x1f8001e,0x1e03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e000f,0x80f0ff00,0x1ffff80,0xffffc00,0x7fffe003,
03471       0xffff001f,0xfff800ff,0xffc007ff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,
03472       0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x3c7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801f01f,
03473       0x1e0007,0x807c07c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03474       0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00000,0xfff003f0,0x1f00f03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x7ff80000,0x3,
03475       0xc0001e0f,0x3c0001e,0x7e,0x1f,0x1e00780,0x3e0,0x7e000f00,0x78000f,0x7800f,0xff9e0000,0x0,0x3fc00,0x0,0x7f,0x8003c000,0x781e0070,
03476       0x3e00f803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3e0f8000,0x7800001e,0x7c0f078,0x1e1e03c0,
03477       0x7807ff,0xf000f000,0x1e07807f,0xfe,0xf000,0xf0003c0,0x781e003,0xc71c7800,0x3ef00000,0x78000007,0xc000003c,0x1e0,0x1e0,0x0,
03478       0x0,0x0,0x1ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,
03479       0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7ff80,0x3c000,0x3c003c0,0xf00f003,0xc1f07800,0x1fc0000f,0x1e00000,0xf8000ff0,0xf0,
03480       0xff,0xffffff,0x80000000,0x3fffc000,0xfff9fe00,0x0,0xf000000,0x0,0x18,0xc0,0x0,0x1f0000,0x1fc0,0x1c00003,0xc00f000f,0xff9fe000,
03481       0x7c0003ff,0xe7f81fff,0x3fc00000,0x0,0x0,0x0,0xfe000,0x1ffffc0f,0xfffffc00,0x0,0xff,0xf0000000,0x700f007f,0xff0fff9f,0xe0000f80,
03482       0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00fff,0xffc00000,0xf800,0xf0000000,0x6380000,0xc0ffff80,0x3e078000,0x1e00,0x7ff80303,
03483       0x83c06000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,0x0,0x7f,0xff00001e,0x7c1fff0,0xfff80,0x7ffc00,0x3f0000,0x7c01f00,
03484       0x3e00f801,0xf007c00f,0x803e007c,0x1f003e0,0xf803c07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
03485       0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f8001e,0x3c03c0f0,0x3c0780,0x1e03c00,0xf01e000,
03486       0x78000780,0x1e001f,0xf07f80,0x3ffff80,0x1ffffc00,0xffffe007,0xffff003f,0xfff801ff,0xffc03fff,0xffc0f000,0x1fffff,0xc0fffffe,
03487       0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,
03488       0xfffffe1e,0x787803c,0x3c01e0,0x1e00f00,0xf007800,0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03489       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x3ff80fc0,0x7fc1e01f,
03490       0x7800000,0x3c00,0x1e00,0x0,0x7fffff80,0x0,0x7ff80000,0x7,0x80001e00,0x3c0001e,0xfc,0xf,0x1e00780,0x1e0,0x7c000f00,0x78000f,
03491       0x78007,0xff1e0000,0x0,0x3ff00,0x0,0x1ff,0x8003c000,0x781e0070,0x3c007803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,
03492       0x80007800,0x780,0x3c07c000,0x7800001e,0x7c0f078,0xf1e03c0,0x780780,0xf000,0x1e07801f,0x3e,0xf000,0xf0003c0,0x781e003,0xcf1c7800,
03493       0x3cf80000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,0x0,0x0,0x3ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,
03494       0x80003c00,0xf000,0x7ff8000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3fff0,0x3c000,0x3c003c0,0xf81f003,
03495       0xc3b87800,0xf80000f,0x1e00001,0xf0000ff0,0xf0,0xff,0xf03fff,0x80000000,0x3fff8001,0xfff1ff00,0x0,0xf000000,0x0,0x18,0xc0,
03496       0x0,0x380000,0x7c0,0x3c00003,0xc00f001f,0xff1ff000,0xf80007ff,0xc7fc3ffe,0x3fe00000,0x0,0x0,0x0,0x1ff000,0x7ffffe1f,0xffffff00,
03497       0x0,0x7f,0xfe000000,0x780f007f,0xff1fff1f,0xf0001f00,0x1e000,0x0,0x780001,0xe0180000,0xf000001c,0xe00fff,0xffc00000,0x7c00,
03498       0xf0000000,0x31c0001,0x80ffff80,0x3e078000,0x1e00,0x7ff80183,0x81c0c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,
03499       0x0,0x7f,0xff00001e,0x7c7ff03,0xc03ff8fe,0x1ffc0f0,0x7e0000,0x7800f00,0x3c007801,0xe003c00f,0x1e0078,0xf003c0,0x7803c07,0x8003c000,
03500       0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,
03501       0xf0001e0,0x78000f00,0x3fc001e,0x7803c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e007f,0xf03fe0,0x7ffff80,0x3ffffc01,
03502       0xffffe00f,0xffff007f,0xfff803ff,0xffc07fff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,
03503       0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x707803c,0x3c01e0,0x1e00f00,0xf007800,
03504       0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03505       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x30f81f00,0xffe1e00f,0x87800000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,
03506       0x7,0x80001e00,0x3c0001e,0x1f8,0x7,0x83c00780,0x1e0,0x7c000f00,0xf8001e,0x3c001,0xfc1e0000,0x0,0x7fe0,0x0,0xffc,0x3c000,0x781e0070,
03507       0x3ffff803,0xc000783c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x380f078,0xf1e03c0,
03508       0x780780,0xf000,0x1e07800f,0x8000001e,0xf000,0xf0003c0,0x3c3c003,0xcf1e7800,0x7c780000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,
03509       0x0,0x0,0x7f003c01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7f7c000,0x1e0000f,0x3c0f01e,
03510       0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfff8,0x3c000,0x3c003c0,0x781e003,0xc3b87800,0x1fc00007,0x83e00003,0xe0000ff8,0xf0,
03511       0x1ff,0xc007fe,0x0,0x7fff8001,0xffe3ff00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x3c0,0x7800003,0xc00f001f,0xfe3ff000,0xf80007ff,
03512       0x8ffc3ffc,0x7fe00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x1f,0xff000000,0x3c0f007f,0xff1ffe3f,0xf0003e00,0x1e000,0x0,0x780001,
03513       0xe0180000,0xf000001e,0x1e00fff,0xffc00000,0x3f00,0xf0000000,0x31c0001,0x80ffff80,0x1f03c000,0x1e00,0x7ff80183,0x81c0c000,
03514       0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x7f,0xff00003c,0xf87f007,0xc03f83ff,0x81fc01f0,0x7c0000,0x7ffff00,0x3ffff801,
03515       0xffffc00f,0xfffe007f,0xfff003ff,0xff807fff,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
03516       0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf003c0f0,0x3c0780,0x1e03c00,0xf01e000,
03517       0x78000780,0x1ffffe,0xf00ff0,0xfe00780,0x7f003c03,0xf801e01f,0xc00f00fe,0x7807f0,0x3c0ffff,0xffc0f000,0x1fffff,0xc0fffffe,
03518       0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,
03519       0x1e,0xf07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783e,0x1e0007,0x801e0f80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03520       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x307c0801,0xe1f1e00f,0x87000000,
03521       0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,0xf,0x1e00,0x3c0001e,0x3f0,0x7,0x83fffffc,0x1e0,0x7c000f00,0xf0001e,0x3c000,0x3e0000,
03522       0x0,0x1ffc,0x1fffff,0xf0007ff0,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x3c000,0x781e0007,0x80007800,
03523       0x780,0x3c03e000,0x7800001e,0xf078,0x79e03c0,0x780780,0xf000,0x1e078007,0x8000000f,0xf000,0xf0003c0,0x3c3c001,0xee0ef000,
03524       0xf87c0000,0x7800001f,0x3c,0x78,0x1e0,0x0,0x0,0x0,0x7c003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00,
03525       0xf000,0x7e3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x1ffc,0x3c000,0x3c003c0,0x781e003,0xe3b8f800,
03526       0x1fc00007,0x83c00007,0xc00000fc,0xf0,0x3e0,0x8001f8,0x0,0x7800000,0xffc7fe00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,
03527       0xf000003,0xc00f000f,0xfc7fe001,0xf00003ff,0x1ff81ff8,0xffc00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x3,0xff800000,0x1e0f0078,
03528       0xffc7f,0xe0007c00,0x1e000,0x0,0x780001,0xe0180000,0xf000000e,0x1c00007,0x80000000,0x1f81,0xe0000000,0x38e0003,0x80000000,
03529       0xf81f000,0x1e00,0x7ff801c3,0x80e1c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf8,0x1f070007,0xc03803ff,0xc1c001f0,
03530       0xf80000,0xfffff00,0x7ffff803,0xffffc01f,0xfffe00ff,0xfff007ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,
03531       0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f00f,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,0xf003c0f0,
03532       0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1ffffc,0xf003f8,0xf800780,0x7c003c03,0xe001e01f,0xf00f8,0x7807c0,0x3c0fc1e,0xf000,
03533       0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,
03534       0xf0003c0,0x78001e00,0x1e,0x1e07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783c,0x1e0007,0x801e0f00,0x0,0x0,0x0,0x0,0x0,0x0,
03535       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xffff8000,0x303c0001,
03536       0xc071e007,0xcf000000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0xf,0xf00,0x780001e,0x7e0,0x7,0x83fffffc,0x1e0,0x7c000f00,0x1f0001e,
03537       0x3c000,0x3c0000,0x0,0x3ff,0x801fffff,0xf003ff80,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,
03538       0x80007800,0x780,0x3c01f000,0x7800001e,0xf078,0x79e03c0,0xf00780,0xf000,0x3e078007,0xc000000f,0xf000,0xf0003c0,0x3c3c001,
03539       0xee0ef000,0xf03e0000,0x7800003e,0x3c,0x78,0x1e0,0x0,0x0,0x0,0xf8003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,
03540       0x80003c00,0xf000,0x7c3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfc,0x3c000,0x3c003c0,0x3c3e001,0xe7b8f000,
03541       0x3fe00007,0xc7c0000f,0xc000003e,0xf0,0x7c0,0x0,0x0,0x7c00000,0x7fcffc00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,0x1e000003,
03542       0xc00f0007,0xfcffc003,0xe00001ff,0x3ff00ff9,0xff800000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x1f800000,0xf0f0078,0x7fcff,
03543       0xc000fc00,0x1e000,0x0,0x780001,0xe0180000,0xf000000f,0x87c00007,0x80000000,0xfe3,0xe0000000,0x18780c3,0x0,0x7c0f800,0x1e00,
03544       0xc3,0x80e18000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x1f0,0x3e00000f,0xc0000303,0xe00003f0,0xf00000,0xfffff80,
03545       0x7ffffc03,0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,
03546       0x3c000001,0xe0001e00,0x780f00f,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1f0f801f,0xe00780f0,0x3c0780,0x1e03c00,
03547       0xf01e000,0x78000780,0x1ffff8,0xf000f8,0x1f000780,0xf8003c07,0xc001e03e,0xf01f0,0x780f80,0x3c1f01e,0xf000,0x1e0000,0xf00000,
03548       0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,
03549       0x1e,0x3c07803c,0x3c01e0,0x1e00f00,0xf007800,0x78007c7c,0x1e0007,0x801f1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03550       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c00000,0x303c0003,0x8039e003,0xef000000,
03551       0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0xfc0,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,
03552       0x0,0x7f,0xe01fffff,0xf00ffc00,0x3c000,0x781f00f0,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,0x80007800,
03553       0x780,0x3c01f000,0x7800001e,0xf078,0x7de01e0,0xf00780,0x7800,0x3c078003,0xc000000f,0xf000,0xf0003c0,0x3e7c001,0xee0ef001,
03554       0xf01e0000,0x7800003e,0x3c,0x3c,0x1e0,0x0,0x0,0x0,0xf0003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00,
03555       0xf000,0x781f000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0x7df00003,
03556       0xc780000f,0x8000003e,0xf0,0x780,0x0,0x0,0x3c00000,0x3fcff800,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x1f00fc,0x1e0,0x1e000001,
03557       0xe00f0003,0xfcff8003,0xe00000ff,0x3fe007f9,0xff000000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x7c00000,0xf0f0078,0x3fcff,0x8000f800,
03558       0x1e000,0x0,0x780001,0xe0180000,0xf000001f,0xffe00007,0x8000003c,0x7ff,0xc0000000,0x1c3ffc7,0x0,0x3e07c00,0x1e00,0xe3,0x80738000,
03559       0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x3e0,0x7c00001d,0xc0000001,0xe0000770,0x1f00000,0xfffff80,0x7ffffc03,
03560       0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
03561       0xe0001e00,0x780f00f,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0x3e07c01f,0xc00780f0,0x3c0780,0x1e03c00,0xf01e000,
03562       0x78000780,0x1fffc0,0xf0007c,0x1e000780,0xf0003c07,0x8001e03c,0xf01e0,0x780f00,0x3c1e01e,0xf000,0x1e0000,0xf00000,0x7800000,
03563       0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1e,0x7807803c,
03564       0x3c01e0,0x1e00f00,0xf007800,0x78003c78,0x1e0007,0x800f1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03565       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x83c00000,0x303c0003,0x8039e001,0xee000000,0x1e00,0x3c00,
03566       0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0x1f80,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,0x0,0x1f,0xfc1fffff,
03567       0xf07ff000,0x0,0x780f00f0,0x78003c03,0xc000781e,0x1e0,0xf803c0,0x1e00,0x1e000,0x781e0007,0x80007800,0x780,0x3c00f800,0x7800001e,
03568       0xf078,0x3de01e0,0xf00780,0x7800,0x3c078003,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfe0ff003,0xe01f0000,0x7800007c,0x3c,0x3c,
03569       0x1e0,0x0,0x0,0x0,0xf0007c01,0xe000f80f,0x800001e0,0xf80f00,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x780f800,0x1e0000f,
03570       0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003c00,0x1e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0xf8f80003,0xe780001f,0x1e,
03571       0xf0,0x780,0x0,0x0,0x3c00000,0x1ffff000,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x3bc1de,0x1e0,0xf000001,0xe00f0001,0xffff0007,0xc000007f,
03572       0xffc003ff,0xfe000000,0x0,0x0,0x0,0xfe000,0x0,0x0,0x0,0x0,0x3c00000,0x1e0f0078,0x1ffff,0x1f000,0x1e000,0x0,0x780000,0xf0180000,
03573       0xf000001f,0xfff00007,0x8000003c,0x1ff,0x80000000,0xe0ff0e,0x0,0x1f03e00,0x1e00,0x70,0x70000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,
03574       0xe1c00,0x0,0x0,0x0,0x7c0,0xf8000019,0xc0000000,0xe0000670,0x1e00000,0xf000780,0x78003c03,0xc001e01e,0xf00f0,0x780780,0x3c0f807,
03575       0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf80f007,0xbc03c001,0xe01e000f,
03576       0xf00078,0x78003c0,0x3c001e00,0x7c03e00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,
03577       0xf0007c07,0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0xf800,0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,
03578       0xf0001e00,0x7803c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1f8001f,0xf00f803c,0x3c01e0,0x1e00f00,0xf007800,
03579       0x78003e78,0x1e000f,0x800f9e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03580       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x3c00000,0x303c0003,0x8039f001,0xfe000000,0x1e00,0x3c00,0x0,0x1e0000,0x0,0x0,0x3c,0xf00,
03581       0x780001e,0x3f00,0x7,0x80000780,0x3e0,0x3e000f00,0x3c0001e,0x3c000,0x7c0000,0x0,0x3,0xfe000000,0xff8000,0x0,0x3c0f81f0,0xf0001e03,
03582       0xc000780f,0x1e0,0xf003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x780,0x3c007c00,0x7800001e,0xf078,0x3de01e0,0xf00780,0x7800,
03583       0x3c078001,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfc07f003,0xe00f0000,0x78000078,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01,
03584       0xf000f007,0x800000f0,0xf80780,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,
03585       0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78001,0xe71df000,0xf8f80001,0xef80003e,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,
03586       0xfffe000,0x0,0x3e000000,0x0,0x18,0x7fff,0xc0000000,0x60c306,0x1e0,0x7800001,0xe00f0000,0xfffe0007,0x8000003f,0xff8001ff,
03587       0xfc000000,0x0,0x0,0x0,0x7c000,0x0,0x0,0x0,0x0,0x3c00000,0x3c0f0078,0xfffe,0x3e000,0x1e000,0x0,0x780000,0xf0180000,0xf000003c,
03588       0xfcf80007,0x8000003c,0x7f,0x0,0x70001c,0x0,0xf81f00,0x0,0x38,0xe0000,0x0,0x0,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf81,
03589       0xf0000039,0xc0000000,0xe0000e70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,0x8000f000,0x78000,
03590       0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f007,0xbc03c001,0xe01e000f,0xf00078,0x78003c0,
03591       0x3c001e00,0xf801f00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,0x8003e03c,
03592       0x1f01e0,0xf80f00,0x7c1e01e,0x7800,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,
03593       0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xe00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef8,0x1f000f,
03594       0x7be00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03595       0x0,0x0,0xf,0x3c00000,0x307c0003,0x8038f000,0xfc000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e00003c,0x780,0xf00001e,
03596       0x7e00,0xf,0x80000780,0x3c0,0x3e001e00,0x3c0001f,0x7c000,0x780007,0xe000003f,0x0,0xfe000000,0xfe0000,0x0,0x3c07c3f0,0xf0001e03,
03597       0xc000f80f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x4000f80,0x3c003c00,0x7800001e,0xf078,0x1fe01f0,0x1f00780,
03598       0x7c00,0x7c078001,0xf000001f,0xf000,0xf0003c0,0x1e78001,0xfc07f007,0xc00f8000,0x780000f8,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01,
03599       0xf000f007,0xc00000f0,0xf80780,0x3c,0x1f003,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,
03600       0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78000,0xfe0fe001,0xf07c0001,0xef00007c,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,
03601       0x7cfc000,0xfc00000,0x3c00000f,0xc3f00000,0x18,0x7fff,0xc0000000,0x406303,0x3e0,0x3c00001,0xf00f0000,0x7cfc000f,0x8000001f,
03602       0x3f0000f9,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x780700f8,0x7cfc,0x7c000,0x1e000,0x0,0x780000,0xf8180000,
03603       0xf0000070,0x3c0007,0x8000003c,0x3f,0x80000000,0x3c0078,0x0,0x780f00,0x0,0x1e,0x3c0000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,0xe1c00,
03604       0x0,0x0,0x0,0xf01,0xe0000071,0xc0000000,0xe0001c70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,
03605       0x8000f800,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00f003,0xfc03e003,0xe01f001f,
03606       0xf800f8,0x7c007c0,0x3e003e01,0xf000f80f,0xf00f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,
03607       0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0x7c00,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,
03608       0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xc00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef0,
03609       0x1f000f,0x7bc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03610       0x0,0x0,0x0,0x0,0x780000,0xf,0x3800040,0x30780003,0x8038f800,0x78000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078,
03611       0x780,0x1f00001e,0xfc00,0x20001f,0x780,0x80007c0,0x1f001e00,0x7c0000f,0x78000,0xf80007,0xe000003f,0x0,0x1e000000,0xf00000,
03612       0x3c000,0x3c03fff0,0xf0001e03,0xc001f007,0x800101e0,0x7e003c0,0x1e00,0x7800,0x781e0007,0x80007800,0x6000f00,0x3c003e00,0x7800001e,
03613       0xf078,0x1fe00f0,0x1e00780,0x3c00,0x78078000,0xf020001e,0xf000,0x7800780,0xff0001,0xfc07f00f,0x8007c000,0x780001f0,0x3c,0xf,
03614       0x1e0,0x0,0x0,0x0,0xf800fc01,0xf801f007,0xc00100f8,0x1f807c0,0x40003c,0xf807,0xf0078007,0x80003c00,0xf000,0x7803e00,0x1f0000f,
03615       0x3c0f01e,0x1e01f0,0x3e007e0,0x7c07c00,0xfc003c00,0x1e,0x3e000,0x3e007c0,0x1ff8000,0xfe0fe003,0xe03e0001,0xff0000fc,0x1e,
03616       0xf0,0x780,0x0,0x0,0x1f00080,0x3cf8000,0xfc00000,0x3c00001f,0x83f00000,0x18,0xc0,0x0,0xc06203,0x40003c0,0x1c00000,0xf80f0000,
03617       0x3cf8001f,0xf,0x3e000079,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x700780fc,0x3cf8,0xfc000,0x1e000,0x0,0x780000,
03618       0x7c180000,0xf0000020,0x100007,0x8000003c,0xf,0x80000000,0x1f01f0,0x0,0x380700,0x0,0xf,0x80f80000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,
03619       0xe1c00,0x0,0x0,0x0,0xe01,0xc0000071,0xc0000001,0xc0001c70,0x1e00040,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,
03620       0x80007800,0x10078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00f003,0xfc01e003,0xc00f001e,
03621       0x7800f0,0x3c00780,0x1e003c00,0xe000700f,0x800f0078,0x7803c0,0x3c01e00,0x1e00f000,0xf0000780,0x1e0000,0xf0003c,0x1f001f80,
03622       0xf800fc07,0xc007e03e,0x3f01f0,0x1f80f80,0xfc1e01f,0x7c00,0x100f8000,0x807c0004,0x3e00020,0x1f000100,0x780000,0x3c00000,0x1e000000,
03623       0xf0000f80,0x1f003c00,0x3c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,0x1f8000f,0x801f003e,0x7c01f0,0x3e00f80,0x1f007c00,
03624       0xf8001ff0,0x1f801f,0x7fc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03625       0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0xf,0x7800078,0x31f80001,0xc070fc00,0xfc000000,0x1e00,0x7c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078,
03626       0x7c0,0x1f00001e,0x1f000,0x38003f,0x780,0xe000f80,0x1f803e00,0x780000f,0x800f8000,0x1f00007,0xe000003f,0x0,0x2000000,0x800000,
03627       0x3c000,0x3e01ff71,0xf0001f03,0xc007f007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x781e0007,0x80007800,0x7801f00,0x3c001f00,0x7800001e,
03628       0xf078,0xfe00f8,0x3e00780,0x3e00,0xf8078000,0xf838003e,0xf000,0x7c00f80,0xff0000,0xfc07e00f,0x8003c000,0x780001e0,0x3c,0xf,
03629       0x1e0,0x0,0x0,0x0,0xf801fc01,0xfc03e003,0xe003007c,0x3f803e0,0x1c0003c,0xfc0f,0xf0078007,0x80003c00,0xf000,0x7801f00,0xf8000f,
03630       0x3c0f01e,0x1e00f8,0x7c007f0,0xf803e01,0xfc003c00,0x8003e,0x1f000,0x1e00fc0,0xff0000,0xfe0fe007,0xc01f0000,0xfe0000f8,0x1e,
03631       0xf0,0x780,0x0,0x0,0xf80180,0x1cf0000,0x1f800000,0x3c00001f,0x83e00000,0x18,0xc0,0x0,0xc06203,0x70007c0,0xe00000,0x7e0f0000,
03632       0x1cf0001e,0x7,0x3c000039,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x7c00000,0xe00780fc,0x2001cf0,0xf8000,0x1e000,0x0,
03633       0x780000,0x7e182000,0xf0000000,0x7,0x8000003c,0x7,0xc0000000,0x7ffc0,0x0,0x180300,0x0,0x3,0xffe00000,0x0,0x0,0x0,0x0,0x0,
03634       0x3f00fc0,0xe1c00,0x0,0x0,0x0,0xc01,0x800000e1,0xc0000003,0xc0003870,0x1f001c0,0x3e0003e1,0xf0001f0f,0x8000f87c,0x7c3e0,0x3e1f00,
03635       0x1f1e007,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e03,0xfc00f001,0xfc01f007,
03636       0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x4000201f,0xc01f007c,0xf803e0,0x7c01f00,0x3e00f801,0xf0000780,0x1e0000,0xf0007c,
03637       0x1f003f80,0xf801fc07,0xc00fe03e,0x7f01f0,0x3f80f80,0x1fc1f03f,0x803e00,0x3007c003,0x803e001c,0x1f000e0,0xf800700,0x780000,
03638       0x3c00000,0x1e000000,0xf00007c0,0x3e003c00,0x3c01f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e001e,0xfc00f0,
03639       0x7e00780,0x3f003c01,0xf8000fe0,0x1fc03e,0x3f800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03640       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,0xfff00001,0xe0f07f03,0xfe000000,0xf00,0x7800,0x0,
03641       0x1e0000,0xfc0000,0x0,0x7e0000f0,0x3f0,0x7e000fff,0xfc03ffff,0xf83f00fe,0x780,0xfc03f80,0xfc0fc00,0xf800007,0xe03f0018,0x7e00007,
03642       0xe000003f,0x0,0x0,0x0,0x3c000,0x1e007c71,0xe0000f03,0xffffe003,0xf01f01ff,0xff8003ff,0xffe01e00,0x3f01,0xf81e0007,0x803ffff0,
03643       0x7e03f00,0x3c000f00,0x7ffffe1e,0xf078,0xfe007e,0xfc00780,0x1f83,0xf0078000,0x783f00fe,0xf000,0x3f03f00,0xff0000,0xfc07e01f,
03644       0x3e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7e07fc01,0xfe07e001,0xf80f007e,0x7f801f8,0xfc0003c,0x7ffe,0xf0078007,
03645       0x807ffffe,0xf000,0x7801f00,0xfff00f,0x3c0f01e,0x1e00fc,0xfc007f8,0x1f803f03,0xfc003c00,0xf80fc,0x1fff0,0x1f83fc0,0xff0000,
03646       0xfc07e007,0xc01f0000,0xfe0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfe0780,0xfe0000,0x1f000000,0x3c00001f,0x7c00e03,0x81c00018,
03647       0xc0,0x0,0x406203,0x7e01fc0,0x700000,0x7fffff80,0xfe0003f,0xffffc003,0xf800001f,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f0,
03648       0x1f800001,0xc007c1fe,0x6000fe0,0x1ffffe,0x1e000,0x0,0x780000,0x3f98e03f,0xffff8000,0x7,0x8000003c,0x7,0xc0000000,0xfe00,
03649       0x0,0x80100,0x0,0x0,0x7f000000,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3f83fe8,0xe1c00,0x0,0x0,0x0,0x801,0xc1,0xc0000007,0x80003070,
03650       0xfc0fc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc03f01,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,
03651       0xffff001f,0xfff800ff,0xffc01fff,0xf800f001,0xfc00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,0x1f,0xf07e003f,0x3f001f8,
03652       0x1f800fc0,0xfc007e07,0xe0000780,0x1e0000,0xf301f8,0xfc0ff80,0x7e07fc03,0xf03fe01f,0x81ff00fc,0xff807e0,0x7fc0f87f,0x81801f80,
03653       0xf003f01f,0x801f80fc,0xfc07e0,0x7e03f00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff807e0,0x7e003c00,0x3c01f81f,0x800fc0fc,0x7e07e0,
03654       0x3f03f00,0x1f81f800,0x1f8000f,0xe07e001f,0x83fc00fc,0x1fe007e0,0xff003f07,0xf8000fe0,0x1fe07e,0x3f800,0x0,0x0,0x0,0x0,0x0,
03655       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,
03656       0xffe00000,0xffe03fff,0xdf000000,0xf00,0x7800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0x1ff,0xfc000fff,0xfc03ffff,0xf83ffffc,0x780,
03657       0xfffff00,0x7fff800,0xf000007,0xffff001f,0xffe00007,0xe000003f,0x0,0x0,0x0,0x3c000,0x1e000001,0xe0000f03,0xffffc001,0xffff01ff,
03658       0xff0003ff,0xffe01e00,0x1fff,0xf81e0007,0x803ffff0,0x7fffe00,0x3c000f80,0x7ffffe1e,0xf078,0xfe003f,0xff800780,0xfff,0xf0078000,
03659       0x7c3ffffc,0xf000,0x3ffff00,0xff0000,0xf803e01e,0x1e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7fffbc01,0xffffc000,
03660       0xffff003f,0xfff800ff,0xffc0003c,0x3ffe,0xf0078007,0x807ffffe,0xf000,0x7800f80,0x7ff00f,0x3c0f01e,0x1e007f,0xff8007ff,0xff001fff,
03661       0xbc003c00,0xffffc,0x1fff0,0x1fffbc0,0xff0000,0x7c07c00f,0x800f8000,0x7e0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7fff80,0x7c0000,
03662       0x1f000000,0x3c00001e,0x7c00f07,0xc1e00018,0xc0,0x0,0x60e303,0x7ffff80,0x380000,0x3fffff80,0x7c0003f,0xffffc001,0xf000000f,
03663       0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff800003,0x8003ffff,0xfe0007c0,0x1ffffe,0x1e000,0x0,0x780000,0x1fffe03f,0xffff8000,
03664       0x7,0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3fffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x1c1,
03665       0xc000000f,0x7070,0x7fffc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,
03666       0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000f001,0xfc007fff,0x3fff8,0x1fffc0,0xfffe00,0x7fff000,0x3b,0xfffc003f,
03667       0xfff001ff,0xff800fff,0xfc007fff,0xe0000780,0x1e0000,0xf3fff8,0xffff780,0x7fffbc03,0xfffde01f,0xffef00ff,0xff7807ff,0xfbc0ffff,
03668       0xff800fff,0xf001ffff,0x800ffffc,0x7fffe0,0x3ffff00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff803ff,0xfc003c00,0x3c00ffff,0x7fff8,
03669       0x3fffc0,0x1fffe00,0xffff000,0x1f,0xfffc001f,0xffbc00ff,0xfde007ff,0xef003fff,0x780007e0,0x1ffffc,0x1f800,0x0,0x0,0x0,0x0,
03670       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x700003f,
03671       0xffc00000,0x7fc01fff,0x9f800000,0xf80,0xf800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0xff,0xf8000fff,0xfc03ffff,0xf83ffff8,0x780,
03672       0xffffe00,0x7fff000,0xf000003,0xfffe001f,0xffc00007,0xe000003f,0x0,0x0,0x0,0x3c000,0xf000003,0xe0000f83,0xffff0000,0xffff01ff,
03673       0xfc0003ff,0xffe01e00,0xfff,0xf01e0007,0x803ffff0,0x7fffc00,0x3c0007c0,0x7ffffe1e,0xf078,0x7e003f,0xff000780,0x7ff,0xe0078000,
03674       0x3c3ffff8,0xf000,0x1fffe00,0x7e0000,0xf803e03e,0x1f000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x3fff3c01,0xefff8000,
03675       0x7ffe001f,0xff78007f,0xff80003c,0x1ffc,0xf0078007,0x807ffffe,0xf000,0x78007c0,0x3ff00f,0x3c0f01e,0x1e003f,0xff0007bf,0xfe000fff,
03676       0xbc003c00,0xffff8,0xfff0,0xfff3c0,0x7e0000,0x7c07c01f,0x7c000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3fff80,0x380000,
03677       0x3e000000,0x7c00003e,0x7801f07,0xc1e00018,0xc0,0x0,0x39c1ce,0x7ffff00,0x1c0000,0xfffff80,0x380003f,0xffffc000,0xe0000007,
03678       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff000007,0x1ffcf,0xfe000380,0x1ffffe,0x1e000,0x0,0x780000,0xfffe03f,0xffff8000,0x7,
03679       0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x381,
03680       0xc000001e,0xe070,0x7fff80,0x7c0001f3,0xe0000f9f,0x7cf8,0x3e7c0,0x1f3e00,0xfbe007,0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,
03681       0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000f000,0xfc007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x79,0xfff8001f,
03682       0xffe000ff,0xff0007ff,0xf8003fff,0xc0000780,0x1e0000,0xf3fff0,0x7ffe780,0x3fff3c01,0xfff9e00f,0xffcf007f,0xfe7803ff,0xf3c07ff3,
03683       0xff8007ff,0xe000ffff,0x7fff8,0x3fffc0,0x1fffe00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff801ff,0xf8003c00,0x3c007ffe,0x3fff0,
03684       0x1fff80,0xfffc00,0x7ffe000,0x1d,0xfff8000f,0xff3c007f,0xf9e003ff,0xcf001ffe,0x780007c0,0x1efff8,0x1f000,0x0,0x0,0x0,0x0,
03685       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0xf000003,
03686       0xfe000000,0x1f000fff,0xfc00000,0x780,0xf000,0x0,0x0,0xf80000,0x0,0x7e0001e0,0x7f,0xf0000fff,0xfc03ffff,0xf81ffff0,0x780,
03687       0x7fff800,0x1ffe000,0x1f000000,0xfff8001f,0xff000007,0xe000003e,0x0,0x0,0x0,0x3c000,0xf800003,0xc0000783,0xfff80000,0x3ffe01ff,
03688       0xe00003ff,0xffe01e00,0x7ff,0xc01e0007,0x803ffff0,0x3fff800,0x3c0003c0,0x7ffffe1e,0xf078,0x7e000f,0xfe000780,0x3ff,0xc0078000,
03689       0x3e1fffe0,0xf000,0x7ff800,0x7e0000,0xf803e07c,0xf800,0x780003ff,0xfffc003c,0x3,0xc00001e0,0x0,0x0,0x0,0xffe3c01,0xe7ff0000,
03690       0x3ffc000f,0xfe78003f,0xfe00003c,0x7f0,0xf0078007,0x807ffffe,0xf000,0x78003e0,0xff00f,0x3c0f01e,0x1e001f,0xfe00079f,0xfc0007ff,
03691       0x3c003c00,0x7ffe0,0x1ff0,0x7fe3c0,0x7e0000,0x7c07c03e,0x3e000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfff00,0x100000,
03692       0x3e000000,0x7800003c,0xf800f07,0xc1e00018,0xc0,0x0,0x1f80fc,0x3fffc00,0xc0000,0x3ffff80,0x100003f,0xffffc000,0x40000002,
03693       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xfc000006,0xff87,0xfc000100,0x1ffffe,0x1e000,0x0,0x780000,0x3ffc03f,0xffff8000,0x7,
03694       0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dff9f8,0xe1c00,0x0,0x0,0x0,0x0,0x3ff,
03695       0xf800003c,0xfffe,0x1ffe00,0x780000f3,0xc000079e,0x3cf0,0x1e780,0xf3c00,0x7bc007,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,
03696       0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,0xf000,0xfc001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x70,0xfff00007,
03697       0xff80003f,0xfc0001ff,0xe0000fff,0x780,0x1e0000,0xf3ffe0,0x1ffc780,0xffe3c00,0x7ff1e003,0xff8f001f,0xfc7800ff,0xe3c03fe1,
03698       0xff0003ff,0xc0007ffc,0x3ffe0,0x1fff00,0xfff800,0xfffffc07,0xffffe03f,0xffff01ff,0xfff800ff,0xf0003c00,0x3c003ffc,0x1ffe0,
03699       0xfff00,0x7ff800,0x3ffc000,0x38,0xfff00007,0xfe3c003f,0xf1e001ff,0x8f000ffc,0x780007c0,0x1e7ff0,0x1f000,0x0,0x0,0x0,0x0,0x0,
03700       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,
03701       0x1fc,0x0,0x780,0xf000,0x0,0x0,0x1f80000,0x0,0x1e0,0x1f,0xc0000000,0x0,0x1ff80,0x0,0xffc000,0x7f8000,0x0,0x3fe00007,0xfc000000,
03702       0x7e,0x0,0x0,0x0,0x0,0x7c00000,0x0,0x0,0xff00000,0x0,0x0,0xfe,0x0,0x0,0x3fc000,0x0,0x0,0x0,0x3,0xf8000000,0xff,0xc0000000,
03703       0x1ff00,0x0,0x1fe000,0x0,0x0,0x0,0x0,0x3c,0x3,0xc00001e0,0x0,0x0,0x0,0x3f80000,0x1fc0000,0x7f00003,0xf8000007,0xf0000000,
03704       0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x7,0xf8000787,0xf00001fc,0x3c000000,0x7f80,0x0,0x1f8000,0x0,0x0,0x0,0x7c000000,0x1e,
03705       0xf0,0x780,0x0,0x0,0x3fc00,0x0,0x3c000000,0x7800003c,0xf000601,0xc00018,0xc0,0x0,0x0,0x3fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03706       0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf0000000,0x7e03,0xf0000000,0x0,0x0,0x0,0x0,0xfe0000,0x0,0x0,0x3c,0x2007,0x80000000,0x0,0x0,
03707       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c7e0f0,0xe1c00,0x0,0x3800000,0x0,0x0,0x3ff,0xf8000078,0xfffe,0x7f800,0x0,0x0,0x0,0x0,
03708       0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,0x7f0000,0x70,0x3fc00001,0xfe00000f,0xf000007f,
03709       0x800003fc,0x0,0x0,0xff00,0x7f0000,0x3f80000,0x1fc00000,0xfe000007,0xf000003f,0x80001f80,0xfc00007f,0xfe0,0x7f00,0x3f800,
03710       0x1fc000,0x0,0x0,0x0,0x3f,0xc0000000,0xff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x78,0x3fc00001,0xf800000f,0xc000007e,0x3f0,0x7c0,
03711       0x1e1fc0,0x1f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03712       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03713       0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xe0000000,0x0,0x0,0x0,
03714       0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,
03715       0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,0x1e,0xf0,0x780,0x0,0x0,0x0,0x0,0x3c000000,0x78000078,0xf000000,0x18,0xc0,0x0,0x0,0x0,
03716       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3c0f,0x80000000,
03717       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0x1800000,0x0,0x0,0x3ff,0xf80000f0,0xfffe,0x0,0x0,0x0,0x0,
03718       0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03719       0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x780,0x1e0000,0x1e000,0x0,0x0,0x0,
03720       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,
03721       0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x1f80000,
03722       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,
03723       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000000,
03724       0x1f,0xf0,0xf80,0x0,0x0,0x0,0x0,0x78000000,0xf8000078,0x1e000000,0x8,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03725       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3fff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03726       0x0,0x3c00000,0xe1c00,0x0,0x1c00000,0x0,0x0,0x1,0xc00001e0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03727       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03728       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x1e0000,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03729       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x3c000,0x0,0x0,0x1f00000,
03730       0x0,0x780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0xfe0100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03731       0x0,0x0,0x0,0x0,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0xf0007fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,
03732       0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x1f,0x800000f0,0x1f80,0x0,0x0,0x0,0x0,
03733       0x78000000,0xf0000070,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03734       0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3ffe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,
03735       0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03736       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03737       0x0,0x0,0x0,0xf00,0x1e0000,0x3c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03738       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x7c000,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03739       0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x7fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,
03740       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4003,0xe0000000,0x0,0x1f000,0x0,0x0,
03741       0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x1,0xf0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0x70000001,0xf00000e0,
03742       0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,
03743       0x0,0x0,0x3c,0xff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,0x0,0x0,0x1,0xc00003ff,
03744       0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03745       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00,0x1e0000,
03746       0x7c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03747       0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0xf0,0x78000,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0,
03748       0x0,0x0,0x0,0x1fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,
03749       0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780f,0xc0000000,0x0,0x3e000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,
03750       0x0,0x0,0x0,0x0,0x3,0xe0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0xf0000103,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03751       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,
03752       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x21e00000,0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,
03753       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,0x0,
03754       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e00,0x1e0000,0xf8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03755       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,
03756       0xf8,0xf8000,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x1fe00,0x0,0x0,0x0,0x0,
03757       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,
03758       0x0,0x0,0x7fff,0xc0000000,0x0,0x3ffe000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0xe0000000,0x7,0xfc0000f0,
03759       0x3fe00,0x0,0x0,0x0,0x0,0x600001ff,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03760       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,
03761       0x3fe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03762       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03763       0x0,0x0,0x0,0x0,0x7fe00,0x1e0000,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03764       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03765       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03766       0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff,0x80000000,0x0,0x3ffc000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,
03767       0x0,0x0,0x0,0x0,0x7f,0xc0000000,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x0,0x0,0x1ff,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03768       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03769       0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3fc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,
03770       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,0x0,
03771       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fc00,0x1e0000,0x1ff0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03772       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03773       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03774       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x3ffe,0x0,0x0,0x3ff8000,0x0,0x0,0x0,
03775       0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0x80000000,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x80000000,0x0,0x0,0x0,0x0,
03776       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03777       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,
03778       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,0x0,
03779       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f800,0x1e0000,0x1fe0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03780       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03781       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03782       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8,0x0,0x0,0x3fe0000,
03783       0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,
03784       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03785       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03786       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03787       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x1e0000,0x1f80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03788       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03789       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03790       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03791       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03792       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03793       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03794       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03795       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03796       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03797       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03798       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,
03799       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03800       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03801       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
03802       0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
03803 
03804     // Definition of a 40x38 'danger' color logo.
03805     const unsigned char logo40x38[4576] = {
03806       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,
03807       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,
03808       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,
03809       1,123,123,0,7,255,255,0,1,0,0,0,2,123,123,123,28,200,200,200,1,123,123,0,8,255,255,0,1,189,189,189,1,0,0,0,
03810       2,123,123,123,27,200,200,200,1,123,123,0,9,255,255,0,1,0,0,0,2,123,123,123,26,200,200,200,1,123,123,0,10,255,
03811       255,0,1,189,189,189,1,0,0,0,2,123,123,123,25,200,200,200,1,123,123,0,3,255,255,0,1,189,189,189,3,0,0,0,1,189,
03812       189,189,3,255,255,0,1,0,0,0,2,123,123,123,24,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,3,255,255,0,1,189,
03813       189,189,1,0,0,0,2,123,123,123,23,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,4,255,255,0,1,0,0,0,2,123,123,123,
03814       22,200,200,200,1,123,123,0,5,255,255,0,5,0,0,0,4,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,21,200,200,200,
03815       1,123,123,0,5,255,255,0,5,0,0,0,5,255,255,0,1,0,0,0,2,123,123,123,20,200,200,200,1,123,123,0,6,255,255,0,5,0,0,
03816       0,5,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,19,200,200,200,1,123,123,0,6,255,255,0,1,123,123,0,3,0,0,0,1,
03817       123,123,0,6,255,255,0,1,0,0,0,2,123,123,123,18,200,200,200,1,123,123,0,7,255,255,0,1,189,189,189,3,0,0,0,1,189,
03818       189,189,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,17,200,200,200,1,123,123,0,8,255,255,0,3,0,0,0,8,255,255,
03819       0,1,0,0,0,2,123,123,123,16,200,200,200,1,123,123,0,9,255,255,0,1,123,123,0,1,0,0,0,1,123,123,0,8,255,255,0,1,189,
03820       189,189,1,0,0,0,2,123,123,123,15,200,200,200,1,123,123,0,9,255,255,0,1,189,189,189,1,0,0,0,1,189,189,189,9,255,255,
03821       0,1,0,0,0,2,123,123,123,14,200,200,200,1,123,123,0,11,255,255,0,1,0,0,0,10,255,255,0,1,189,189,189,1,0,0,0,2,123,
03822       123,123,13,200,200,200,1,123,123,0,23,255,255,0,1,0,0,0,2,123,123,123,12,200,200,200,1,123,123,0,11,255,255,0,1,189,
03823       189,189,2,0,0,0,1,189,189,189,9,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,11,200,200,200,1,123,123,0,11,255,255,
03824       0,4,0,0,0,10,255,255,0,1,0,0,0,2,123,123,123,10,200,200,200,1,123,123,0,12,255,255,0,4,0,0,0,10,255,255,0,1,189,189,
03825       189,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,12,255,255,0,1,189,189,189,2,0,0,0,1,189,189,189,11,255,255,0,1,
03826       0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,27,255,255,0,1,0,0,0,3,123,123,123,8,200,200,200,1,123,123,0,26,255,
03827       255,0,1,189,189,189,1,0,0,0,3,123,123,123,9,200,200,200,1,123,123,0,24,255,255,0,1,189,189,189,1,0,0,0,4,123,123,
03828       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,
03829       200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0};
03830 
03831     //! Set/get output stream for CImg library messages.
03832     inline std::FILE* output(std::FILE *file) {
03833       static std::FILE *res = stderr;
03834       if (file) res = file;
03835       return res;
03836     }
03837 
03838     //! Display a warning message.
03839     /**
03840        \param format is a C-string describing the format of the message, as in <tt>std::printf()</tt>.
03841     **/
03842     inline void warn(const char *const format, ...) {
03843       if (cimg::exception_mode()>=1) {
03844         char message[8192] = { 0 };
03845         std::va_list ap;
03846         va_start(ap,format);
03847         std::vsprintf(message,format,ap);
03848         va_end(ap);
03849 #ifdef cimg_strict_warnings
03850         throw CImgWarningException(message);
03851 #else
03852         std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s %s",cimg::t_red,cimg::t_normal,message);
03853 #endif
03854       }
03855     }
03856 
03857     // Execute an external system command.
03858     /**
03859        \note This function is similar to <tt>std::system()</tt>
03860        and is here because using the <tt>std::</tt> version on
03861        Windows may open undesired consoles.
03862     **/
03863     inline int system(const char *const command, const char *const module_name=0) {
03864 #if cimg_OS==2
03865       PROCESS_INFORMATION pi;
03866       STARTUPINFO si;
03867       std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
03868       std::memset(&si,0,sizeof(STARTUPINFO));
03869       GetStartupInfo(&si);
03870       si.cb = sizeof(si);
03871       si.wShowWindow = SW_HIDE;
03872       si.dwFlags |= SW_HIDE;
03873       const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi);
03874       if (res) {
03875         WaitForSingleObject(pi.hProcess, INFINITE);
03876         CloseHandle(pi.hThread);
03877         CloseHandle(pi.hProcess);
03878         return 0;
03879       } else
03880 #endif
03881         return std::system(command);
03882       return module_name?0:1;
03883     }
03884 
03885     //! Return a reference to a temporary variable of type T.
03886     template<typename T>
03887     inline T& temporary(const T&) {
03888       static T temp;
03889       return temp;
03890     }
03891 
03892     //! Exchange values of variables \p a and \p b.
03893     template<typename T>
03894     inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
03895 
03896     //! Exchange values of variables (\p a1,\p a2) and (\p b1,\p b2).
03897     template<typename T1, typename T2>
03898     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
03899       cimg::swap(a1,b1); cimg::swap(a2,b2);
03900     }
03901 
03902     //! Exchange values of variables (\p a1,\p a2,\p a3) and (\p b1,\p b2,\p b3).
03903     template<typename T1, typename T2, typename T3>
03904     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
03905       cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
03906     }
03907 
03908     //! Exchange values of variables (\p a1,\p a2,...,\p a4) and (\p b1,\p b2,...,\p b4).
03909     template<typename T1, typename T2, typename T3, typename T4>
03910     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
03911       cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
03912     }
03913 
03914     //! Exchange values of variables (\p a1,\p a2,...,\p a5) and (\p b1,\p b2,...,\p b5).
03915     template<typename T1, typename T2, typename T3, typename T4, typename T5>
03916     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
03917       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
03918     }
03919 
03920     //! Exchange values of variables (\p a1,\p a2,...,\p a6) and (\p b1,\p b2,...,\p b6).
03921     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
03922     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) {
03923       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
03924     }
03925 
03926     //! Exchange values of variables (\p a1,\p a2,...,\p a7) and (\p b1,\p b2,...,\p b7).
03927     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
03928     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6,
03929                      T7& a7, T7& b7) {
03930       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
03931     }
03932 
03933     //! Exchange values of variables (\p a1,\p a2,...,\p a8) and (\p b1,\p b2,...,\p b8).
03934     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
03935     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6,
03936                      T7& a7, T7& b7, T8& a8, T8& b8) {
03937       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
03938     }
03939 
03940     //! Return the current endianness of the CPU.
03941     /**
03942        \return \c false for "Little Endian", \c true for "Big Endian".
03943     **/
03944     inline bool endianness() {
03945       const int x = 1;
03946       return ((unsigned char*)&x)[0]?false:true;
03947     }
03948 
03949     //! Invert endianness of a memory buffer.
03950     template<typename T>
03951     inline void invert_endianness(T* const buffer, const unsigned int size) {
03952       if (size) switch (sizeof(T)) {
03953       case 1 : break;
03954       case 2 : { for (unsigned short *ptr = (unsigned short*)buffer+size; ptr>(unsigned short*)buffer; ) {
03955         const unsigned short val = *(--ptr);
03956         *ptr = (unsigned short)((val>>8)|((val<<8)));
03957       }
03958       } break;
03959       case 4 : { for (unsigned int *ptr = (unsigned int*)buffer+size; ptr>(unsigned int*)buffer; ) {
03960         const unsigned int val = *(--ptr);
03961         *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24);
03962       }
03963       } break;
03964       default : { for (T* ptr = buffer+size; ptr>buffer; ) {
03965         unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
03966         for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
03967       }
03968       }
03969       }
03970     }
03971 
03972     //! Invert endianness of a single variable.
03973     template<typename T>
03974     inline T& invert_endianness(T& a) {
03975       invert_endianness(&a,1);
03976       return a;
03977     }
03978 
03979     //! Get the value of a system timer with a millisecond precision.
03980     inline unsigned long time() {
03981 #if cimg_OS==1
03982       struct timeval st_time;
03983       gettimeofday(&st_time,0);
03984       return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000);
03985 #elif cimg_OS==2
03986       static SYSTEMTIME st_time;
03987       GetSystemTime(&st_time);
03988       return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour)));
03989 #else
03990       return 0;
03991 #endif
03992     }
03993 
03994     //! Sleep for a certain numbers of milliseconds.
03995     /**
03996        This function frees the CPU ressources during the sleeping time.
03997        It may be used to temporize your program properly, without wasting CPU time.
03998     **/
03999     inline void sleep(const unsigned int milliseconds) {
04000 #if cimg_OS==1
04001       struct timespec tv;
04002       tv.tv_sec = milliseconds/1000;
04003       tv.tv_nsec = (milliseconds%1000)*1000000;
04004       nanosleep(&tv,0);
04005 #elif cimg_OS==2
04006       Sleep(milliseconds);
04007 #endif
04008     }
04009 
04010     inline unsigned int _sleep(const unsigned int milliseconds, unsigned long& timer) {
04011       if (!timer) timer = cimg::time();
04012       const unsigned long current_time = cimg::time();
04013       if (current_time>=timer+milliseconds) { timer = current_time; return 0; }
04014       const unsigned long time_diff = timer + milliseconds - current_time;
04015       timer = current_time + time_diff;
04016       cimg::sleep(time_diff);
04017       return (unsigned int)time_diff;
04018     }
04019 
04020     //! Wait for a certain number of milliseconds since the last call.
04021     /**
04022        This function is equivalent to sleep() but the waiting time is computed with regard to the last call
04023        of wait(). It may be used to temporize your program properly.
04024     **/
04025     inline unsigned int wait(const unsigned int milliseconds) {
04026       static unsigned long timer = 0;
04027       if (!timer) timer = cimg::time();
04028       return _sleep(milliseconds,timer);
04029     }
04030 
04031     // Use a specific srand initialization to avoid multi-threads to have to the
04032     // same series of random numbers (executed only once for a single program).
04033     inline void srand() {
04034       static bool first_time = true;
04035       if (first_time) {
04036         std::srand(cimg::time());
04037         unsigned char *const rand_ptr = new unsigned char[1+std::rand()%2048];
04038         std::srand((unsigned int)std::rand() + *(unsigned int*)(void*)rand_ptr);
04039         delete[] rand_ptr;
04040         first_time = false;
04041       }
04042     }
04043 
04044     //! Return a left bitwise-rotated number.
04045     template<typename T>
04046     inline T rol(const T a, const unsigned int n=1) {
04047       return n?(T)((a<<n)|(a>>((sizeof(T)<<3)-n))):a;
04048     }
04049 
04050     inline float rol(const float a, const unsigned int n=1) {
04051       return (float)rol((int)a,n);
04052     }
04053 
04054     inline double rol(const double a, const unsigned int n=1) {
04055       return (double)rol((long)a,n);
04056     }
04057 
04058     //! Return a right bitwise-rotated number.
04059     template<typename T>
04060     inline T ror(const T a, const unsigned int n=1) {
04061       return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a;
04062     }
04063 
04064     inline float ror(const float a, const unsigned int n=1) {
04065       return (float)ror((int)a,n);
04066     }
04067 
04068     inline double ror(const double a, const unsigned int n=1) {
04069       return (double)ror((long)a,n);
04070     }
04071 
04072     //! Return the absolute value of a number.
04073     /**
04074        \note This function is different from <tt>std::abs()</tt> or <tt>std::fabs()</tt>
04075        because it is able to consider a variable of any type, without cast needed.
04076     **/
04077     template<typename T>
04078     inline T abs(const T a) {
04079       return a>=0?a:-a;
04080     }
04081     inline bool abs(const bool a) {
04082       return a;
04083     }
04084     inline unsigned char abs(const unsigned char a) {
04085       return a;
04086     }
04087     inline unsigned short abs(const unsigned short a) {
04088       return a;
04089     }
04090     inline unsigned int abs(const unsigned int a) {
04091       return a;
04092     }
04093     inline unsigned long abs(const unsigned long a) {
04094       return a;
04095     }
04096     inline double abs(const double a) {
04097       return std::fabs(a);
04098     }
04099     inline float abs(const float a) {
04100       return (float)std::fabs((double)a);
04101     }
04102     inline int abs(const int a) {
04103       return std::abs(a);
04104     }
04105 
04106     //! Return the square of a number.
04107     template<typename T>
04108     inline T sqr(const T val) {
04109       return val*val;
04110     }
04111 
04112     //! Return 1 + log_10(x).
04113     inline int xln(const int x) {
04114       return x>0?(int)(1+std::log10((double)x)):1;
04115     }
04116 
04117     //! Return the minimum value between two numbers.
04118     template<typename t1, typename t2>
04119     inline typename cimg::superset<t1,t2>::type min(const t1& a, const t2& b) {
04120       typedef typename cimg::superset<t1,t2>::type t1t2;
04121       return (t1t2)(a<=b?a:b);
04122     }
04123 
04124     //! Return the minimum value between three numbers.
04125     template<typename t1, typename t2, typename t3>
04126     inline typename cimg::superset2<t1,t2,t3>::type min(const t1& a, const t2& b, const t3& c) {
04127       typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
04128       return (t1t2t3)cimg::min(cimg::min(a,b),c);
04129     }
04130 
04131     //! Return the minimum value between four numbers.
04132     template<typename t1, typename t2, typename t3, typename t4>
04133     inline typename cimg::superset3<t1,t2,t3,t4>::type min(const t1& a, const t2& b, const t3& c, const t4& d) {
04134       typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
04135       return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d);
04136     }
04137 
04138     //! Return the maximum value between two numbers.
04139     template<typename t1, typename t2>
04140     inline typename cimg::superset<t1,t2>::type max(const t1& a, const t2& b) {
04141       typedef typename cimg::superset<t1,t2>::type t1t2;
04142       return (t1t2)(a>=b?a:b);
04143     }
04144 
04145     //! Return the maximum value between three numbers.
04146     template<typename t1, typename t2, typename t3>
04147     inline typename cimg::superset2<t1,t2,t3>::type max(const t1& a, const t2& b, const t3& c) {
04148       typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
04149       return (t1t2t3)cimg::max(cimg::max(a,b),c);
04150     }
04151 
04152     //! Return the maximum value between four numbers.
04153     template<typename t1, typename t2, typename t3, typename t4>
04154     inline typename cimg::superset3<t1,t2,t3,t4>::type max(const t1& a, const t2& b, const t3& c, const t4& d) {
04155       typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
04156       return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d);
04157     }
04158 
04159     //! Return the sign of a number.
04160     template<typename T>
04161     inline T sign(const T x) {
04162       return (x<0)?(T)(-1):(x==0?(T)0:(T)1);
04163     }
04164 
04165     //! Return the nearest power of 2 higher than a given number.
04166     template<typename T>
04167     inline unsigned int nearest_pow2(const T x) {
04168       unsigned int i = 1;
04169       while (x>i) i<<=1;
04170       return i;
04171     }
04172 
04173     //! Return the sinc() of a given number.
04174     inline double sinc(const double x) {
04175       return x?std::sin(x)/x:1;
04176     }
04177 
04178     //! Return the modulo of a number.
04179     /**
04180        \note This modulo function accepts negative and floating-points modulo numbers, as well as
04181        variable of any type.
04182     **/
04183     template<typename T>
04184     inline T mod(const T& x, const T& m) {
04185       const double dx = (double)x, dm = (double)m;
04186       if (x<0) { return (T)(dm+dx+dm*std::floor(-dx/dm)); }
04187       return (T)(dx-dm*std::floor(dx/dm));
04188     }
04189     inline int mod(const bool x, const bool m) {
04190       return m?(x?1:0):0;
04191     }
04192     inline int mod(const char x, const char m) {
04193       return x>=0?x%m:(x%m?m+x%m:0);
04194     }
04195     inline int mod(const short x, const short m) {
04196       return x>=0?x%m:(x%m?m+x%m:0);
04197     }
04198     inline int mod(const int x, const int m) {
04199       return x>=0?x%m:(x%m?m+x%m:0);
04200     }
04201     inline int mod(const long x, const long m) {
04202       return x>=0?x%m:(x%m?m+x%m:0);
04203     }
04204     inline int mod(const unsigned char x, const unsigned char m) {
04205       return x%m;
04206     }
04207     inline int mod(const unsigned short x, const unsigned short m) {
04208       return x%m;
04209     }
04210     inline int mod(const unsigned int x, const unsigned int m) {
04211       return x%m;
04212     }
04213     inline int mod(const unsigned long x, const unsigned long m) {
04214       return x%m;
04215     }
04216 
04217     //! Return the minmod of two numbers.
04218     /**
04219        <i>minmod(\p a,\p b)</i> is defined to be :
04220        - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
04221        - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
04222     **/
04223     template<typename T>
04224     inline T minmod(const T a, const T b) {
04225       return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
04226     }
04227 
04228     //! Return a random variable between [0,1] with respect to an uniform distribution.
04229     inline double rand() {
04230       static bool first_time = true;
04231       if (first_time) { cimg::srand(); first_time = false; }
04232       return (double)std::rand()/RAND_MAX;
04233     }
04234 
04235     //! Return a random variable between [-1,1] with respect to an uniform distribution.
04236     inline double crand() {
04237       return 1-2*cimg::rand();
04238     }
04239 
04240     //! Return a random variable following a gaussian distribution and a standard deviation of 1.
04241     inline double grand() {
04242       double x1, w;
04243       do {
04244         const double x2 = 2*cimg::rand() - 1.0;
04245         x1 = 2*cimg::rand()-1.0;
04246         w = x1*x1 + x2*x2;
04247       } while (w<=0 || w>=1.0);
04248       return x1*std::sqrt((-2*std::log(w))/w);
04249     }
04250 
04251     //! Return a random variable following a Poisson distribution of parameter z.
04252     inline unsigned int prand(const double z) {
04253       if (z<=1.0e-10) return 0;
04254       if (z>100.0) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z);
04255       unsigned int k = 0;
04256       const double y = std::exp(-z);
04257       for (double s = 1.0; s>=y; ++k) s*=cimg::rand();
04258       return k-1;
04259     }
04260 
04261     //! Return a rounded number.
04262     /**
04263        \param x is the number to be rounded.
04264        \param y is the rounding precision.
04265        \param rounding_type defines the type of rounding (0=nearest, -1=backward, 1=forward).
04266     **/
04267     inline double round(const double x, const double y, const int rounding_type=0) {
04268       if (y<=0) return x;
04269       const double delta = cimg::mod(x,y);
04270       if (delta==0.0) return x;
04271       const double
04272         backward = x - delta,
04273         forward = backward + y;
04274       return rounding_type<0?backward:(rounding_type>0?forward:(2*delta<y?backward:forward));
04275     }
04276 
04277     inline double _pythagore(double a, double b) {
04278       const double absa = cimg::abs(a), absb = cimg::abs(b);
04279       if (absa>absb) { const double tmp = absb/absa; return absa*std::sqrt(1.0+tmp*tmp); }
04280       else { const double tmp = absa/absb; return (absb==0?0:absb*std::sqrt(1.0+tmp*tmp)); }
04281     }
04282 
04283     //! Remove the 'case' of an ASCII character.
04284     inline char uncase(const char x) {
04285       return (char)((x<'A'||x>'Z')?x:x-'A'+'a');
04286     }
04287 
04288     //! Remove the 'case' of a C string.
04289     /**
04290        Acts in-place.
04291     **/
04292     inline void uncase(char *const string) {
04293       if (string) for (char *ptr = string; *ptr; ++ptr) *ptr = uncase(*ptr);
04294     }
04295 
04296     //! Read a double number from a C-string.
04297     /**
04298        \note This function is quite similar to <tt>std::atof()</tt>,
04299        but that it allows the retrieval of fractions as in "1/2".
04300     **/
04301     inline double atof(const char *const str) {
04302       double x = 0, y = 1;
04303       if (!str) return 0; else { std::sscanf(str,"%lf/%lf",&x,&y); return x/y; }
04304     }
04305 
04306     //! Compare the first \p n characters of two C-strings, ignoring the case.
04307     /**
04308        \note This function is defined since it is not provided by all compilers
04309        (not an ANSI function).
04310     **/
04311     inline int strncasecmp(const char *const s1, const char *const s2, const int l) {
04312       if (!l) return 0;
04313       if (!s1) return s2?-1:0;
04314       const char *ns1 = s1, *ns2 = s2;
04315       int k, diff = 0; for (k = 0; k<l && !(diff = uncase(*ns1)-uncase(*ns2)); ++k) { ++ns1; ++ns2; }
04316       return k!=l?diff:0;
04317     }
04318 
04319     //! Compare two C-strings, ignoring the case.
04320     /**
04321        \note This function is defined since it is not provided by all compilers
04322        (not an ANSI function).
04323     **/
04324     inline int strcasecmp(const char *const s1, const char *const s2) {
04325       if (!s1) return s2?-1:0;
04326       const unsigned int l1 = std::strlen(s1), l2 = std::strlen(s2);
04327       return cimg::strncasecmp(s1,s2,1+(l1<l2?l1:l2));
04328     }
04329 
04330     //! Remove useless delimiters on the borders of a C-string
04331     inline bool strpare(char *const s, const char delimiter=' ', const bool symmetric=false, const bool is_iterative=false) {
04332       if (!s) return false;
04333       const int l = (int)std::strlen(s);
04334       int p, q;
04335       if (symmetric) for (p = 0, q = l-1; p<q && s[p]==delimiter && s[q]==delimiter; ) { --q; ++p; if (!is_iterative) break; }
04336       else {
04337         for (p = 0; p<l && s[p]==delimiter; ) { ++p; if (!is_iterative) break; }
04338         for (q = l-1; q>p && s[q]==delimiter; ) { --q; if (!is_iterative) break; }
04339       }
04340       const int n = q - p + 1;
04341       if (n!=l) { std::memmove(s,s+p,n); s[n] = 0; return true; }
04342       return false;
04343     }
04344 
04345     //! Replace explicit escape sequences '\x' in C-strings.
04346     inline void strescape(char *const s) {
04347 #define cimg_strescape(ci,co) case ci: *nd = co; ++ns; break;
04348       static unsigned int val = 0;
04349       for (char *ns = s, *nd = s; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) {
04350             cimg_strescape('n','\n');
04351             cimg_strescape('t','\t');
04352             cimg_strescape('v','\v');
04353             cimg_strescape('b','\b');
04354             cimg_strescape('r','\r');
04355             cimg_strescape('f','\f');
04356             cimg_strescape('a','\a');
04357             cimg_strescape('\\','\\');
04358             cimg_strescape('\?','\?');
04359             cimg_strescape('\'','\'');
04360             cimg_strescape('\"','\"');
04361           case 0 : *nd = 0; break;
04362           case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
04363             std::sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns;
04364             *nd = val; break;
04365           case 'x':
04366             std::sscanf(++ns,"%x",&val); while ((*ns>='0' && *ns<='7') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns;
04367             *nd = val; break;
04368           default : *nd = *(ns++);
04369           } else *nd = *(ns++);
04370     }
04371 
04372     //! Compute the basename of a filename.
04373     inline const char* basename(const char *const s)  {
04374       const char *p = 0;
04375       for (const char *np = s; np>=s && (p=np); np = std::strchr(np,cimg_file_separator)+1) {}
04376       return p;
04377     }
04378 
04379     // Generate a random filename.
04380     inline const char* filenamerand() {
04381       static char randomid[9] = { 0,0,0,0,0,0,0,0,0 };
04382       cimg::srand();
04383       for (unsigned int k = 0; k<8; ++k) {
04384         const int v = (int)std::rand()%3;
04385         randomid[k] = (char)(v==0?('0'+(std::rand()%10)):(v==1?('a'+(std::rand()%26)):('A'+(std::rand()%26))));
04386       }
04387       return randomid;
04388     }
04389 
04390     // Convert filename into a Windows-style filename.
04391     inline void winformat_string(char *const s) {
04392       if (s && s[0]) {
04393 #if cimg_OS==2
04394         char *const ns = new char[MAX_PATH];
04395         if (GetShortPathNameA(s,ns,MAX_PATH)) std::strcpy(s,ns);
04396 #endif
04397       }
04398     }
04399 
04400     //! Return or set path to store temporary files.
04401     inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false) {
04402 #define _cimg_test_temporary_path(p) \
04403       if (!path_found) { \
04404         std::sprintf(st_path,"%s",p); \
04405         std::sprintf(tmp,"%s%c%s",st_path,cimg_file_separator,filetmp); \
04406         if ((file=std::fopen(tmp,"wb"))!=0) { std::fclose(file); std::remove(tmp); path_found = true; } \
04407       }
04408       static char *st_path = 0;
04409       if (reinit_path && st_path) { delete[] st_path; st_path = 0; }
04410       if (user_path) {
04411         if (!st_path) st_path = new char[1024];
04412         std::memset(st_path,0,1024);
04413         std::strncpy(st_path,user_path,1023);
04414       } else if (!st_path) {
04415         st_path = new char[1024];
04416         std::memset(st_path,0,1024);
04417         bool path_found = false;
04418         char tmp[1024] = { 0 }, filetmp[512] = { 0 };
04419         std::FILE *file = 0;
04420         std::sprintf(filetmp,"%s.tmp",cimg::filenamerand());
04421         char *tmpPath = getenv("TMP");
04422         if (!tmpPath) { tmpPath = getenv("TEMP"); winformat_string(tmpPath); }
04423         if (tmpPath) _cimg_test_temporary_path(tmpPath);
04424 #if cimg_OS==2
04425         _cimg_test_temporary_path("C:\\WINNT\\Temp");
04426         _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
04427         _cimg_test_temporary_path("C:\\Temp");
04428         _cimg_test_temporary_path("C:");
04429         _cimg_test_temporary_path("D:\\WINNT\\Temp");
04430         _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
04431         _cimg_test_temporary_path("D:\\Temp");
04432         _cimg_test_temporary_path("D:");
04433 #else
04434         _cimg_test_temporary_path("/tmp");
04435         _cimg_test_temporary_path("/var/tmp");
04436 #endif
04437         if (!path_found) {
04438           st_path[0] = 0;
04439           std::strcpy(tmp,filetmp);
04440           if ((file=std::fopen(tmp,"wb"))!=0) { std::fclose(file); std::remove(tmp); path_found = true; }
04441         }
04442         if (!path_found)
04443           throw CImgIOException("cimg::temporary_path() : Failed to locate path for writing temporary files.\n");
04444       }
04445       return st_path;
04446     }
04447 
04448     // Return or set path to the "Program files/" directory (windows only).
04449 #if cimg_OS==2
04450     inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
04451       static char *st_path = 0;
04452       if (reinit_path && st_path) { delete[] st_path; st_path = 0; }
04453       if (user_path) {
04454         if (!st_path) st_path = new char[1024];
04455         std::memset(st_path,0,1024);
04456         std::strncpy(st_path,user_path,1023);
04457       } else if (!st_path) {
04458         st_path = new char[MAX_PATH];
04459         std::memset(st_path,0,MAX_PATH);
04460         // Note : in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
04461 #if !defined(__INTEL_COMPILER)
04462         if (!SHGetSpecialFolderPathA(0,st_path,0x0026,false)) {
04463           const char *const pfPath = getenv("PROGRAMFILES");
04464           if (pfPath) std::strncpy(st_path,pfPath,MAX_PATH-1);
04465           else std::strcpy(st_path,"C:\\PROGRA~1");
04466         }
04467 #else
04468         std::strcpy(st_path,"C:\\PROGRA~1");
04469 #endif
04470       }
04471       return st_path;
04472     }
04473 #endif
04474 
04475     //! Return or set path to the ImageMagick's \c convert tool.
04476     inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false) {
04477       static char *st_path = 0;
04478       if (reinit_path && st_path) { delete[] st_path; st_path = 0; }
04479       if (user_path) {
04480         if (!st_path) st_path = new char[1024];
04481         std::memset(st_path,0,1024);
04482         std::strncpy(st_path,user_path,1023);
04483       } else if (!st_path) {
04484         st_path = new char[1024];
04485         std::memset(st_path,0,1024);
04486         bool path_found = false;
04487         std::FILE *file = 0;
04488 #if cimg_OS==2
04489         const char *const pf_path = programfiles_path();
04490         if (!path_found) {
04491           std::sprintf(st_path,".\\convert.exe");
04492           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04493         }
04494         for (int k = 32; k>=10 && !path_found; --k) {
04495           std::sprintf(st_path,"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k);
04496           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04497         }
04498         for (int k = 9; k>=0 && !path_found; --k) {
04499           std::sprintf(st_path,"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k);
04500           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04501         }
04502         for (int k = 32; k>=0 && !path_found; --k) {
04503           std::sprintf(st_path,"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k);
04504           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04505         }
04506         for (int k = 32; k>=10 && !path_found; --k) {
04507           std::sprintf(st_path,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k);
04508           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04509         }
04510         for (int k = 9; k>=0 && !path_found; --k) {
04511           std::sprintf(st_path,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k);
04512           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04513         }
04514         for (int k = 32; k>=0 && !path_found; --k) {
04515           std::sprintf(st_path,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k);
04516           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04517         }
04518         for (int k = 32; k>=10 && !path_found; --k) {
04519           std::sprintf(st_path,"C:\\IMAGEM~1.%.2d-\\convert.exe",k);
04520           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04521         }
04522         for (int k = 9; k>=0 && !path_found; --k) {
04523           std::sprintf(st_path,"C:\\IMAGEM~1.%d-Q\\convert.exe",k);
04524           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04525         }
04526         for (int k = 32; k>=0 && !path_found; --k) {
04527           std::sprintf(st_path,"C:\\IMAGEM~1.%d\\convert.exe",k);
04528           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04529         }
04530         for (int k = 32; k>=10 && !path_found; --k) {
04531           std::sprintf(st_path,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
04532           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04533         }
04534         for (int k = 9; k>=0 && !path_found; --k) {
04535           std::sprintf(st_path,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
04536           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04537         }
04538         for (int k = 32; k>=0 && !path_found; --k) {
04539           std::sprintf(st_path,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
04540           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04541         }
04542         for (int k = 32; k>=10 && !path_found; --k) {
04543           std::sprintf(st_path,"D:\\IMAGEM~1.%.2d-\\convert.exe",k);
04544           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04545         }
04546         for (int k = 9; k>=0 && !path_found; --k) {
04547           std::sprintf(st_path,"D:\\IMAGEM~1.%d-Q\\convert.exe",k);
04548           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04549         }
04550         for (int k = 32; k>=0 && !path_found; --k) {
04551           std::sprintf(st_path,"D:\\IMAGEM~1.%d\\convert.exe",k);
04552           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04553         }
04554         for (int k = 32; k>=10 && !path_found; --k) {
04555           std::sprintf(st_path,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
04556           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04557         }
04558         for (int k = 9; k>=0 && !path_found; --k) {
04559           std::sprintf(st_path,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
04560           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04561         }
04562         for (int k = 32; k>=0 && !path_found; --k) {
04563           std::sprintf(st_path,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
04564           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04565         }
04566         if (!path_found) std::strcpy(st_path,"convert.exe");
04567 #else
04568         if (!path_found) {
04569           std::sprintf(st_path,"./convert");
04570           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04571         }
04572         if (!path_found) std::strcpy(st_path,"convert");
04573 #endif
04574         winformat_string(st_path);
04575       }
04576       return st_path;
04577     }
04578 
04579     //! Return path of the GraphicsMagick's \c gm tool.
04580     inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false) {
04581       static char *st_path = 0;
04582       if (reinit_path && st_path) { delete[] st_path; st_path = 0; }
04583       if (user_path) {
04584         if (!st_path) st_path = new char[1024];
04585         std::memset(st_path,0,1024);
04586         std::strncpy(st_path,user_path,1023);
04587       } else if (!st_path) {
04588         st_path = new char[1024];
04589         std::memset(st_path,0,1024);
04590         bool path_found = false;
04591         std::FILE *file = 0;
04592 #if cimg_OS==2
04593         const char *const pf_path = programfiles_path();
04594         if (!path_found) {
04595           std::sprintf(st_path,".\\gm.exe");
04596           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04597         }
04598         for (int k = 32; k>=10 && !path_found; --k) {
04599           std::sprintf(st_path,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
04600           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04601         }
04602         for (int k = 9; k>=0 && !path_found; --k) {
04603           std::sprintf(st_path,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
04604           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04605         }
04606         for (int k = 32; k>=0 && !path_found; --k) {
04607           std::sprintf(st_path,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
04608           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04609         }
04610         for (int k = 32; k>=10 && !path_found; --k) {
04611           std::sprintf(st_path,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
04612           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04613         }
04614         for (int k = 9; k>=0 && !path_found; --k) {
04615           std::sprintf(st_path,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
04616           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04617         }
04618         for (int k = 32; k>=0 && !path_found; --k) {
04619           std::sprintf(st_path,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
04620           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04621         }
04622         for (int k = 32; k>=10 && !path_found; --k) {
04623           std::sprintf(st_path,"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
04624           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04625         }
04626         for (int k = 9; k>=0 && !path_found; --k) {
04627           std::sprintf(st_path,"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
04628           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04629         }
04630         for (int k = 32; k>=0 && !path_found; --k) {
04631           std::sprintf(st_path,"C:\\GRAPHI~1.%d\\gm.exe",k);
04632           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04633         }
04634         for (int k = 32; k>=10 && !path_found; --k) {
04635           std::sprintf(st_path,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
04636           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04637         }
04638         for (int k = 9; k>=0 && !path_found; --k) {
04639           std::sprintf(st_path,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
04640           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04641         }
04642         for (int k = 32; k>=0 && !path_found; --k) {
04643           std::sprintf(st_path,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
04644           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04645         }
04646         for (int k = 32; k>=10 && !path_found; --k) {
04647           std::sprintf(st_path,"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
04648           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04649         }
04650         for (int k = 9; k>=0 && !path_found; --k) {
04651           std::sprintf(st_path,"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
04652           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04653         }
04654         for (int k = 32; k>=0 && !path_found; --k) {
04655           std::sprintf(st_path,"D:\\GRAPHI~1.%d\\gm.exe",k);
04656           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04657         }
04658         for (int k = 32; k>=10 && !path_found; --k) {
04659           std::sprintf(st_path,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
04660           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04661         }
04662         for (int k = 9; k>=0 && !path_found; --k) {
04663           std::sprintf(st_path,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
04664           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04665         }
04666         for (int k = 32; k>=0 && !path_found; --k) {
04667           std::sprintf(st_path,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
04668           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04669         }
04670         if (!path_found) std::strcpy(st_path,"gm.exe");
04671 #else
04672         if (!path_found) {
04673           std::sprintf(st_path,"./gm");
04674           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04675         }
04676         if (!path_found) std::strcpy(st_path,"gm");
04677 #endif
04678         winformat_string(st_path);
04679       }
04680       return st_path;
04681     }
04682 
04683     //! Return or set path of the \c XMedcon tool.
04684     inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false) {
04685       static char *st_path = 0;
04686       if (reinit_path && st_path) { delete[] st_path; st_path = 0; }
04687       if (user_path) {
04688         if (!st_path) st_path = new char[1024];
04689         std::memset(st_path,0,1024);
04690         std::strncpy(st_path,user_path,1023);
04691       } else if (!st_path) {
04692         st_path = new char[1024];
04693         std::memset(st_path,0,1024);
04694         bool path_found = false;
04695         std::FILE *file = 0;
04696 #if cimg_OS==2
04697         const char *const pf_path = programfiles_path();
04698         if (!path_found) {
04699           std::sprintf(st_path,".\\medcon.bat");
04700           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04701         }
04702         if (!path_found) {
04703           std::sprintf(st_path,".\\medcon.exe");
04704           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04705         }
04706         if (!path_found) {
04707           std::sprintf(st_path,"%s\\XMedCon\\bin\\medcon.bat",pf_path);
04708           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04709         }
04710         if (!path_found) {
04711           std::sprintf(st_path,"%s\\XMedCon\\bin\\medcon.exe",pf_path);
04712           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04713         }
04714         if (!path_found) std::strcpy(st_path,"medcon.bat");
04715 #else
04716         if (!path_found) {
04717           std::sprintf(st_path,"./medcon");
04718           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04719         }
04720         if (!path_found) std::strcpy(st_path,"medcon");
04721 #endif
04722         winformat_string(st_path);
04723       }
04724       return st_path;
04725     }
04726 
04727     //! Return or set path to the 'ffmpeg' command.
04728     inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false) {
04729       static char *st_path = 0;
04730       if (reinit_path && st_path) { delete[] st_path; st_path = 0; }
04731       if (user_path) {
04732         if (!st_path) st_path = new char[1024];
04733         std::memset(st_path,0,1024);
04734         std::strncpy(st_path,user_path,1023);
04735       } else if (!st_path) {
04736         st_path = new char[1024];
04737         std::memset(st_path,0,1024);
04738         bool path_found = false;
04739         std::FILE *file = 0;
04740 #if cimg_OS==2
04741         if (!path_found) {
04742           std::sprintf(st_path,".\\ffmpeg.exe");
04743           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04744         }
04745         if (!path_found) std::strcpy(st_path,"ffmpeg.exe");
04746 #else
04747         if (!path_found) {
04748           std::sprintf(st_path,"./ffmpeg");
04749           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04750         }
04751         if (!path_found) std::strcpy(st_path,"ffmpeg");
04752 #endif
04753         winformat_string(st_path);
04754       }
04755       return st_path;
04756     }
04757 
04758     //! Return or set path to the 'gzip' command.
04759     inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false) {
04760       static char *st_path = 0;
04761       if (reinit_path && st_path) { delete[] st_path; st_path = 0; }
04762       if (user_path) {
04763         if (!st_path) st_path = new char[1024];
04764         std::memset(st_path,0,1024);
04765         std::strncpy(st_path,user_path,1023);
04766       } else if (!st_path) {
04767         st_path = new char[1024];
04768         std::memset(st_path,0,1024);
04769         bool path_found = false;
04770         std::FILE *file = 0;
04771 #if cimg_OS==2
04772         if (!path_found) {
04773           std::sprintf(st_path,".\\gzip.exe");
04774           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04775         }
04776         if (!path_found) std::strcpy(st_path,"gzip.exe");
04777 #else
04778         if (!path_found) {
04779           std::sprintf(st_path,"./gzip");
04780           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04781         }
04782         if (!path_found) std::strcpy(st_path,"gzip");
04783 #endif
04784         winformat_string(st_path);
04785       }
04786       return st_path;
04787     }
04788 
04789     //! Return or set path to the 'gunzip' command.
04790     inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false) {
04791       static char *st_path = 0;
04792       if (reinit_path && st_path) { delete[] st_path; st_path = 0; }
04793       if (user_path) {
04794         if (!st_path) st_path = new char[1024];
04795         std::memset(st_path,0,1024);
04796         std::strncpy(st_path,user_path,1023);
04797       } else if (!st_path) {
04798         st_path = new char[1024];
04799         std::memset(st_path,0,1024);
04800         bool path_found = false;
04801         std::FILE *file = 0;
04802 #if cimg_OS==2
04803         if (!path_found) {
04804           std::sprintf(st_path,".\\gunzip.exe");
04805           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04806         }
04807         if (!path_found) std::strcpy(st_path,"gunzip.exe");
04808 #else
04809         if (!path_found) {
04810           std::sprintf(st_path,"./gunzip");
04811           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04812         }
04813         if (!path_found) std::strcpy(st_path,"gunzip");
04814 #endif
04815         winformat_string(st_path);
04816       }
04817       return st_path;
04818     }
04819 
04820     //! Return or set path to the 'dcraw' command.
04821     inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false) {
04822       static char *st_path = 0;
04823       if (reinit_path && st_path) { delete[] st_path; st_path = 0; }
04824       if (user_path) {
04825         if (!st_path) st_path = new char[1024];
04826         std::memset(st_path,0,1024);
04827         std::strncpy(st_path,user_path,1023);
04828       } else if (!st_path) {
04829         st_path = new char[1024];
04830         std::memset(st_path,0,1024);
04831         bool path_found = false;
04832         std::FILE *file = 0;
04833 #if cimg_OS==2
04834         if (!path_found) {
04835           std::sprintf(st_path,".\\dcraw.exe");
04836           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04837         }
04838         if (!path_found) std::strcpy(st_path,"dcraw.exe");
04839 #else
04840         if (!path_found) {
04841           std::sprintf(st_path,"./dcraw");
04842           if ((file=std::fopen(st_path,"r"))!=0) { std::fclose(file); path_found = true; }
04843         }
04844         if (!path_found) std::strcpy(st_path,"dcraw");
04845 #endif
04846         winformat_string(st_path);
04847       }
04848       return st_path;
04849     }
04850 
04851     //! Split a filename into two strings 'body' and 'extension'.
04852     inline const char *split_filename(const char *const filename, char *const body=0) {
04853       if (!filename) { if (body) body[0] = 0; return 0; }
04854       const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.')+1) {}
04855       if (p==filename) {
04856         if (body) std::strcpy(body,filename);
04857         return filename + std::strlen(filename);
04858       }
04859       const unsigned int l = p - filename - 1;
04860       if (body) { std::memcpy(body,filename,l); body[l] = 0; }
04861       return p;
04862     }
04863 
04864     //! Create a numbered version of a filename.
04865     inline char* number_filename(const char *const filename, const int number, const unsigned int n, char *const string) {
04866       if (!filename) { if (string) string[0] = 0; return 0; }
04867       char format[1024] = { 0 }, body[1024] = { 0 };
04868       const char *const ext = cimg::split_filename(filename,body);
04869       if (n>0) std::sprintf(format,"%s_%%.%ud.%s",body,n,ext);
04870       else std::sprintf(format,"%s_%%d.%s",body,ext);
04871       std::sprintf(string,format,number);
04872       return string;
04873     }
04874 
04875     //! Open a file, and check for possible errors.
04876     inline std::FILE *fopen(const char *const path, const char *const mode) {
04877       if (!path)
04878         throw CImgArgumentException("cimg::fopen() : Specified file path is (null).");
04879       if (!mode)
04880         throw CImgArgumentException("cimg::fopen() : File '%s', specified mode is (null).",
04881                                     path);
04882 
04883       if (path[0]=='-') return (*mode=='r')?stdin:stdout;
04884       std::FILE *res = std::fopen(path,mode);
04885       if (!res)
04886         throw CImgIOException("cimg::fopen() : Failed to open file '%s' with mode '%s'.",
04887                               path,mode);
04888       return res;
04889     }
04890 
04891     //! Close a file, and check for possible errors.
04892     inline int fclose(std::FILE *file) {
04893       if (!file) warn("cimg::fclose() : Specified file is (null).");
04894       if (!file || file==stdin || file==stdout) return 0;
04895       const int errn = std::fclose(file);
04896       if (errn!=0) warn("cimg::fclose() : Error code %d returned during file closing.",
04897                         errn);
04898       return errn;
04899     }
04900 
04901     //! Try to guess the image format of a filename, using the magic numbers in its header.
04902     inline const char *file_type(std::FILE *const file, const char *const filename) {
04903       if (!file && !filename)
04904         throw CImgArgumentException("cimg::file_type() : Specified filename is (null).");
04905       static const char
04906         *const _pnm = "pnm",
04907         *const _pfm = "pfm",
04908         *const _bmp = "bmp",
04909         *const _gif = "gif",
04910         *const _jpg = "jpg",
04911         *const _off = "off",
04912         *const _pan = "pan",
04913         *const _png = "png",
04914         *const _tif = "tif",
04915         *const _inr = "inr",
04916         *const _dcm = "dcm";
04917       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
04918       const char *f_type = 0, *head;
04919       char header[2048] = { 0 }, item[1024] = { 0 };
04920       const unsigned char *const uheader = (unsigned char*)header;
04921       int err; char cerr;
04922       const unsigned int siz = (unsigned int)std::fread(header,2048,1,nfile);   // Read first 2048 bytes.
04923       if (!file) cimg::fclose(nfile);
04924 
04925       if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // Check for OFF format.
04926       else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // Check for INRIMAGE format.
04927       else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // Check for PANDORE format.
04928       else if (!std::strncmp(header+128,"DICM",4)) f_type = _dcm; // Check for DICOM format.
04929       else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg;  // Check for JPEG format.
04930       else if (header[0]=='B' && header[1]=='M') f_type = _bmp;  // Check for BMP format.
04931       else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // Check for GIF format.
04932                (header[4]=='7' || header[4]=='9')) f_type = _gif;
04933       else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&  // Check for PNG format.
04934                uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png;
04935       else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // Check for TIFF format.
04936       else { // Check for PNM or PFM format.
04937         head = header;
04938         while (head<header+siz && (err=std::sscanf(head,"%1023[^\n]",item))!=EOF && (item[0]=='#' || !err))
04939           head+=1+(err?std::strlen(item):0);
04940         if (std::sscanf(item," P%d",&err)==1) f_type = _pnm;
04941         else if (std::sscanf(item," P%c",&cerr)==1 && (cerr=='f' || cerr=='F')) f_type = _pfm;
04942       }
04943       return f_type;
04944     }
04945 
04946     //! Read file data, and check for possible errors.
04947     template<typename T>
04948     inline int fread(T *const ptr, const unsigned int nmemb, std::FILE *stream) {
04949       if (!ptr || nmemb<=0 || !stream)
04950         throw CImgArgumentException("cimg::fread() : Invalid reading request of %u %s%s from file %p to buffer %p.",
04951                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
04952 
04953       const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
04954       unsigned int toread = nmemb, alread = 0, ltoread = 0, lalread = 0;
04955       do {
04956         ltoread = (toread*sizeof(T))<wlimitT?toread:wlimit;
04957         lalread = (unsigned int)std::fread((void*)(ptr+alread),sizeof(T),ltoread,stream);
04958         alread+=lalread;
04959         toread-=lalread;
04960       } while (ltoread==lalread && toread>0);
04961       if (toread>0)
04962         warn("cimg::fread() : Only %u/%u elements could be read from file.",
04963              alread,nmemb);
04964       return alread;
04965     }
04966 
04967     //! Write data to a file, and check for possible errors.
04968     template<typename T>
04969     inline int fwrite(const T *ptr, const unsigned int nmemb, std::FILE *stream) {
04970       if (!ptr || !stream)
04971         throw CImgArgumentException("cimg::fwrite() : Invalid writting request of %u %s%s from buffer %p to file %p.",
04972                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
04973       if (nmemb<=0) return 0;
04974       const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
04975       unsigned int towrite = nmemb, alwrite = 0, ltowrite = 0, lalwrite = 0;
04976       do {
04977         ltowrite = (towrite*sizeof(T))<wlimitT?towrite:wlimit;
04978         lalwrite = (unsigned int)std::fwrite((void*)(ptr+alwrite),sizeof(T),ltowrite,stream);
04979         alwrite+=lalwrite;
04980         towrite-=lalwrite;
04981       } while (ltowrite==lalwrite && towrite>0);
04982       if (towrite>0)
04983         warn("cimg::fwrite() : Only %u/%u elements could be written in file.",
04984              alwrite,nmemb);
04985       return alwrite;
04986     }
04987 
04988     inline const char* option(const char *const name, const int argc, const char *const *const argv,
04989                               const char *const defaut, const char *const usage, const bool reset_static) {
04990       static bool first = true, visu = false;
04991       if (reset_static) { first = true; return 0; }
04992       const char *res = 0;
04993       if (first) {
04994         first = false;
04995         visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
04996         visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
04997         visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
04998       }
04999       if (!name && visu) {
05000         if (usage) {
05001           std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
05002           std::fprintf(cimg::output()," : %s",usage);
05003           std::fprintf(cimg::output()," (%s, %s)\n\n",__DATE__,__TIME__);
05004         }
05005         if (defaut) std::fprintf(cimg::output(),"%s\n",defaut);
05006       }
05007       if (name) {
05008         if (argc>0) {
05009           int k = 0;
05010           while (k<argc && std::strcmp(argv[k],name)) ++k;
05011           res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
05012         } else res = defaut;
05013         if (visu && usage) std::fprintf(cimg::output(),"    %s%-16s%s %-24s %s%s%s\n",
05014                                         cimg::t_bold,name,cimg::t_normal,res?res:"0",cimg::t_green,usage,cimg::t_normal);
05015       }
05016       return res;
05017     }
05018 
05019     inline const char* option(const char *const name, const int argc, const char *const *const argv,
05020                               const char *const defaut, const char *const usage=0) {
05021       return option(name,argc,argv,defaut,usage,false);
05022     }
05023 
05024     inline bool option(const char *const name, const int argc, const char *const *const argv,
05025                        const bool defaut, const char *const usage=0) {
05026       const char *const s = cimg::option(name,argc,argv,(char*)0);
05027       const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut;
05028       cimg::option(name,0,0,res?"true":"false",usage);
05029       return res;
05030     }
05031 
05032     inline int option(const char *const name, const int argc, const char *const *const argv,
05033                       const int defaut, const char *const usage=0) {
05034       const char *const s = cimg::option(name,argc,argv,(char*)0);
05035       const int res = s?std::atoi(s):defaut;
05036       char tmp[256] = { 0 };
05037       std::sprintf(tmp,"%d",res);
05038       cimg::option(name,0,0,tmp,usage);
05039       return res;
05040     }
05041 
05042     inline char option(const char *const name, const int argc, const char *const *const argv,
05043                        const char defaut, const char *const usage=0) {
05044       const char *const s = cimg::option(name,argc,argv,(char*)0);
05045       const char res = s?s[0]:defaut;
05046       char tmp[8] = { 0 };
05047       tmp[0] = res;
05048       cimg::option(name,0,0,tmp,usage);
05049       return res;
05050     }
05051 
05052     inline float option(const char *const name, const int argc, const char *const *const argv,
05053                         const float defaut, const char *const usage=0) {
05054       const char *const s = cimg::option(name,argc,argv,(char*)0);
05055       const float res = s?(float)cimg::atof(s):defaut;
05056       char tmp[256] = { 0 };
05057       std::sprintf(tmp,"%g",res);
05058       cimg::option(name,0,0,tmp,usage);
05059       return res;
05060     }
05061 
05062     inline double option(const char *const name, const int argc, const char *const *const argv,
05063                          const double defaut, const char *const usage=0) {
05064       const char *const s = cimg::option(name,argc,argv,(char*)0);
05065       const double res = s?cimg::atof(s):defaut;
05066       char tmp[256] = { 0 };
05067       std::sprintf(tmp,"%g",res);
05068       cimg::option(name,0,0,tmp,usage);
05069       return res;
05070     }
05071 
05072     inline const char* argument(const unsigned int nb, const int argc, const char *const *const argv, const unsigned int nb_singles=0, ...) {
05073       for (int k = 1, pos = 0; k<argc;) {
05074         const char *const item = argv[k];
05075         bool option = (*item=='-'), single_option = false;
05076         if (option) {
05077           va_list ap;
05078           va_start(ap,nb_singles);
05079           for (unsigned int i = 0; i<nb_singles; ++i) if (!cimg::strcasecmp(item,va_arg(ap,char*))) { single_option = true; break; }
05080           va_end(ap);
05081         }
05082         if (option) { ++k; if (!single_option) ++k; }
05083         else { if (pos++==(int)nb) return item; else ++k; }
05084       }
05085       return 0;
05086     }
05087 
05088     //! Print informations about %CImg environement variables.
05089     /**
05090        Printing is done on the standard error output.
05091     **/
05092     inline void info() {
05093       char tmp[1024] = { 0 };
05094       std::fprintf(cimg::output(),"\n %sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags :\n\n",
05095                    cimg::t_red,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
05096                    cimg::t_normal,__DATE__,__TIME__);
05097 
05098       std::fprintf(cimg::output(),"  > Operating System :       %s%-13s%s %s('cimg_OS'=%d)%s\n",
05099                    cimg::t_bold,
05100                    cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"),
05101                    cimg::t_normal,cimg::t_green,
05102                    cimg_OS,
05103                    cimg::t_normal);
05104 
05105       std::fprintf(cimg::output(),"  > CPU endianness :         %s%s Endian%s\n",
05106                    cimg::t_bold,
05107                    cimg::endianness()?"Big":"Little",
05108                    cimg::t_normal);
05109 
05110       std::fprintf(cimg::output(),"  > Verbosity mode :         %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
05111                    cimg::t_bold,
05112                    cimg_verbosity==0?"Quiet":(cimg_verbosity==1?"Console":(cimg_verbosity==2?"Dialog":(cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings"))),
05113                    cimg::t_normal,cimg::t_green,
05114                    cimg_verbosity,
05115                    cimg::t_normal);
05116 
05117       std::fprintf(cimg::output(),"  > Stricts warnings :       %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
05118                    cimg::t_bold,
05119 #ifdef cimg_strict_warnings
05120                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05121 #else
05122                    "No",cimg::t_normal,cimg::t_green,"undefined",
05123 #endif
05124                    cimg::t_normal);
05125 
05126       std::fprintf(cimg::output(),"  > Using VT100 messages :   %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
05127                    cimg::t_bold,
05128 #ifdef cimg_use_vt100
05129                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05130 #else
05131                    "No",cimg::t_normal,cimg::t_green,"undefined",
05132 #endif
05133                    cimg::t_normal);
05134 
05135       std::fprintf(cimg::output(),"  > Display type :           %s%-13s%s %s('cimg_display'=%d)%s\n",
05136                    cimg::t_bold,
05137                    cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
05138                    cimg::t_normal,cimg::t_green,
05139                    cimg_display,
05140                    cimg::t_normal);
05141 
05142 #if cimg_display==1
05143       std::fprintf(cimg::output(),"  > Using XShm for X11 :     %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
05144                    cimg::t_bold,
05145 #ifdef cimg_use_xshm
05146                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05147 #else
05148                    "No",cimg::t_normal,cimg::t_green,"undefined",
05149 #endif
05150                    cimg::t_normal);
05151 
05152       std::fprintf(cimg::output(),"  > Using XRand for X11 :    %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
05153                    cimg::t_bold,
05154 #ifdef cimg_use_xrandr
05155                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05156 #else
05157                    "No",cimg::t_normal,cimg::t_green,"undefined",
05158 #endif
05159                    cimg::t_normal);
05160 #endif
05161       std::fprintf(cimg::output(),"  > Using OpenMP :           %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
05162                    cimg::t_bold,
05163 #ifdef cimg_use_openmp
05164                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05165 #else
05166                    "No",cimg::t_normal,cimg::t_green,"undefined",
05167 #endif
05168                    cimg::t_normal);
05169       std::fprintf(cimg::output(),"  > Using PNG library :      %s%-13s%s %s('cimg_use_png' %s)%s\n",
05170                    cimg::t_bold,
05171 #ifdef cimg_use_png
05172                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05173 #else
05174                    "No",cimg::t_normal,cimg::t_green,"undefined",
05175 #endif
05176                    cimg::t_normal);
05177       std::fprintf(cimg::output(),"  > Using JPEG library :     %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
05178                    cimg::t_bold,
05179 #ifdef cimg_use_jpeg
05180                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05181 #else
05182                    "No",cimg::t_normal,cimg::t_green,"undefined",
05183 #endif
05184                    cimg::t_normal);
05185 
05186       std::fprintf(cimg::output(),"  > Using TIFF library :     %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
05187                    cimg::t_bold,
05188 #ifdef cimg_use_tiff
05189                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05190 #else
05191                    "No",cimg::t_normal,cimg::t_green,"undefined",
05192 #endif
05193                    cimg::t_normal);
05194 
05195       std::fprintf(cimg::output(),"  > Using Magick++ library : %s%-13s%s %s('cimg_use_magick' %s)%s\n",
05196                    cimg::t_bold,
05197 #ifdef cimg_use_magick
05198                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05199 #else
05200                    "No",cimg::t_normal,cimg::t_green,"undefined",
05201 #endif
05202                    cimg::t_normal);
05203 
05204       std::fprintf(cimg::output(),"  > Using FFTW3 library :    %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
05205                    cimg::t_bold,
05206 #ifdef cimg_use_fftw3
05207                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05208 #else
05209                    "No",cimg::t_normal,cimg::t_green,"undefined",
05210 #endif
05211                    cimg::t_normal);
05212 
05213       std::fprintf(cimg::output(),"  > Using LAPACK library :   %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
05214                    cimg::t_bold,
05215 #ifdef cimg_use_lapack
05216                    "Yes",cimg::t_normal,cimg::t_green,"defined",
05217 #else
05218                    "No",cimg::t_normal,cimg::t_green,"undefined",
05219 #endif
05220                    cimg::t_normal);
05221 
05222       std::sprintf(tmp,"\"%.1020s\"",cimg::imagemagick_path());
05223       std::fprintf(cimg::output(),"  > Path of ImageMagick :    %s%-13s%s\n",
05224                    cimg::t_bold,
05225                    tmp,
05226                    cimg::t_normal);
05227 
05228       std::sprintf(tmp,"\"%.1020s\"",cimg::graphicsmagick_path());
05229       std::fprintf(cimg::output(),"  > Path of GraphicsMagick : %s%-13s%s\n",
05230                    cimg::t_bold,
05231                    tmp,
05232                    cimg::t_normal);
05233 
05234       std::sprintf(tmp,"\"%.1020s\"",cimg::medcon_path());
05235       std::fprintf(cimg::output(),"  > Path of 'medcon' :       %s%-13s%s\n",
05236                    cimg::t_bold,
05237                    tmp,
05238                    cimg::t_normal);
05239 
05240       std::sprintf(tmp,"\"%.1020s\"",cimg::temporary_path());
05241       std::fprintf(cimg::output(),"  > Temporary path :         %s%-13s%s\n",
05242                    cimg::t_bold,
05243                    tmp,
05244                    cimg::t_normal);
05245 
05246       std::fprintf(cimg::output(),"\n");
05247     }
05248 
05249     // Declare LAPACK function signatures if necessary.
05250 #ifdef cimg_use_lapack
05251     template<typename T>
05252     inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
05253       dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
05254     }
05255 
05256     inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
05257       sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
05258     }
05259 
05260     template<typename T>
05261     inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
05262       dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
05263     }
05264 
05265     inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
05266       sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
05267     }
05268 
05269     template<typename T>
05270     inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
05271                       T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
05272       dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
05273     }
05274 
05275     inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
05276                       float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
05277       sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
05278     }
05279 
05280     template<typename T>
05281     inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
05282       int one = 1;
05283       dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
05284     }
05285 
05286     inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
05287       int one = 1;
05288       sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
05289     }
05290 
05291     template<typename T>
05292     inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
05293       dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
05294     }
05295 
05296     inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
05297       ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
05298     }
05299 #endif
05300 
05301     // End of the 'cimg' namespace
05302   }
05303 
05304   /*------------------------------------------------
05305    #
05306    #
05307    #   Definition of mathematical operators and
05308    #   external functions.
05309    #
05310    #
05311    -------------------------------------------------*/
05312 
05313 #define _cimg_create_ext_operators(typ) \
05314   template<typename T> \
05315   inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
05316     return img + val; \
05317   } \
05318   template<typename T> \
05319   inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
05320     typedef typename cimg::superset<T,typ>::type Tt; \
05321     return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
05322   } \
05323   template<typename T> \
05324   inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
05325     return img*val; \
05326   } \
05327   template<typename T> \
05328   inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
05329     return val*img.get_invert(); \
05330   } \
05331   template<typename T> \
05332   inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
05333     return img & val; \
05334   } \
05335   template<typename T> \
05336   inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
05337     return img | val; \
05338   } \
05339   template<typename T> \
05340   inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
05341     return img ^ val; \
05342   } \
05343 
05344   _cimg_create_ext_operators(bool)
05345   _cimg_create_ext_operators(unsigned char)
05346   _cimg_create_ext_operators(char)
05347   _cimg_create_ext_operators(signed char)
05348   _cimg_create_ext_operators(unsigned short)
05349   _cimg_create_ext_operators(short)
05350   _cimg_create_ext_operators(unsigned int)
05351   _cimg_create_ext_operators(int)
05352   _cimg_create_ext_operators(unsigned long)
05353   _cimg_create_ext_operators(long)
05354   _cimg_create_ext_operators(float)
05355   _cimg_create_ext_operators(double)
05356 
05357   template<typename T>
05358   inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
05359     return img + expression;
05360   }
05361 
05362   template<typename T>
05363   inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
05364     return (CImg<_cimg_Tfloat>(img._width,img._height,img._depth,img._spectrum)=expression)-=img;
05365   }
05366 
05367   template<typename T>
05368   inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
05369     return img*expression;
05370   }
05371 
05372   template<typename T>
05373   inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
05374     return expression*img.get_invert();
05375   }
05376 
05377   template<typename T>
05378   inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
05379     return img & expression;
05380   }
05381 
05382   template<typename T>
05383   inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
05384     return img | expression;
05385   }
05386 
05387   template<typename T>
05388   inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
05389     return img ^ expression;
05390   }
05391 
05392   template<typename T>
05393   inline CImg<_cimg_Tfloat> sqr(const CImg<T>& instance) {
05394     return instance.get_sqr();
05395   }
05396 
05397   template<typename T>
05398   inline CImg<_cimg_Tfloat> sqrt(const CImg<T>& instance) {
05399     return instance.get_sqrt();
05400   }
05401 
05402   template<typename T>
05403   inline CImg<_cimg_Tfloat> exp(const CImg<T>& instance) {
05404     return instance.get_exp();
05405   }
05406 
05407   template<typename T>
05408   inline CImg<_cimg_Tfloat> log(const CImg<T>& instance) {
05409     return instance.get_log();
05410   }
05411 
05412   template<typename T>
05413   inline CImg<_cimg_Tfloat> log10(const CImg<T>& instance) {
05414     return instance.get_log10();
05415   }
05416 
05417   template<typename T>
05418   inline CImg<_cimg_Tfloat> abs(const CImg<T>& instance) {
05419     return instance.get_abs();
05420   }
05421 
05422   template<typename T>
05423   inline CImg<_cimg_Tfloat> sign(const CImg<T>& instance) {
05424     return instance.get_sign();
05425   }
05426 
05427   template<typename T>
05428   inline CImg<_cimg_Tfloat> cos(const CImg<T>& instance) {
05429     return instance.get_cos();
05430   }
05431 
05432   template<typename T>
05433   inline CImg<_cimg_Tfloat> sin(const CImg<T>& instance) {
05434     return instance.get_sin();
05435   }
05436 
05437   template<typename T>
05438   inline CImg<_cimg_Tfloat> sinc(const CImg<T>& instance) {
05439     return instance.get_sinc();
05440   }
05441 
05442   template<typename T>
05443   inline CImg<_cimg_Tfloat> tan(const CImg<T>& instance) {
05444     return instance.get_tan();
05445   }
05446 
05447   template<typename T>
05448   inline CImg<_cimg_Tfloat> acos(const CImg<T>& instance) {
05449     return instance.get_acos();
05450   }
05451 
05452   template<typename T>
05453   inline CImg<_cimg_Tfloat> asin(const CImg<T>& instance) {
05454     return instance.get_asin();
05455   }
05456 
05457   template<typename T>
05458   inline CImg<_cimg_Tfloat> atan(const CImg<T>& instance) {
05459     return instance.get_atan();
05460   }
05461 
05462   template<typename T>
05463   inline CImg<_cimg_Tfloat> cosh(const CImg<T>& instance) {
05464     return instance.get_cosh();
05465   }
05466 
05467   template<typename T>
05468   inline CImg<_cimg_Tfloat> sinh(const CImg<T>& instance) {
05469     return instance.get_sinh();
05470   }
05471 
05472   template<typename T>
05473   inline CImg<_cimg_Tfloat> tanh(const CImg<T>& instance) {
05474     return instance.get_tanh();
05475   }
05476 
05477   template<typename T>
05478   inline CImg<T> transpose(const CImg<T>& instance) {
05479     return instance.get_transpose();
05480   }
05481 
05482   template<typename T>
05483   inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) {
05484     return instance.get_invert();
05485   }
05486 
05487   template<typename T>
05488   inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance) {
05489     return instance.get_pseudoinvert();
05490   }
05491 
05492   /*-------------------------------------------
05493    #
05494    #
05495    #
05496    # Definition of the CImgDisplay structure
05497    #
05498    #
05499    #
05500    --------------------------------------------*/
05501 
05502   //! This class represents a window which can display \ref CImg images and handles mouse and keyboard events.
05503   /**
05504      Creating a \c CImgDisplay instance opens a window that can be used to display a \c CImg<T> image
05505      of a \c CImgList<T> image list inside. When a display is created, associated window events
05506      (such as mouse motion, keyboard and window size changes) are handled and can be easily
05507      detected by testing specific \c CImgDisplay data fields.
05508      See \ref cimg_displays for a complete tutorial on using the \c CImgDisplay class.
05509   **/
05510 
05511   struct CImgDisplay {
05512 
05513     //! Width of the display.
05514     unsigned int _width;
05515 
05516     //! Height of the display.
05517     unsigned int _height;
05518 
05519     //! Width of the underlying window.
05520     volatile unsigned int _window_width;
05521 
05522     //! Height of the underlying window.
05523     volatile unsigned int _window_height;
05524 
05525     //! X-pos of the display on the screen.
05526     volatile int _window_x;
05527 
05528     //! Y-pos of the display on the screen.
05529     volatile int _window_y;
05530 
05531     //! X-coordinate of the mouse pointer on the display.
05532     volatile int _mouse_x;
05533 
05534     //! Y-coordinate of the mouse pointer on the display.
05535     volatile int _mouse_y;
05536 
05537     //! Normalization type used for the display.
05538     unsigned int _normalization;
05539 
05540     //! Display title.
05541     char *_title;
05542 
05543     //! Button state of the mouse.
05544     volatile unsigned int _button;
05545 
05546     //! Wheel state of the mouse.
05547     volatile int _wheel;
05548 
05549     //! Key value if pressed.
05550     volatile unsigned int _keys[128];
05551     volatile unsigned int _released_keys[128];
05552 
05553     //! Closed state of the window.
05554     volatile bool _is_closed;
05555 
05556     //! Resized state of the window.
05557     volatile bool _is_resized;
05558 
05559     //! Moved state of the window.
05560     volatile bool _is_moved;
05561 
05562     //! Event state of the window.
05563     volatile bool _is_event;
05564 
05565     //! Current state of the corresponding key (exists for all referenced keys).
05566     volatile bool _is_keyESC;
05567     volatile bool _is_keyF1;
05568     volatile bool _is_keyF2;
05569     volatile bool _is_keyF3;
05570     volatile bool _is_keyF4;
05571     volatile bool _is_keyF5;
05572     volatile bool _is_keyF6;
05573     volatile bool _is_keyF7;
05574     volatile bool _is_keyF8;
05575     volatile bool _is_keyF9;
05576     volatile bool _is_keyF10;
05577     volatile bool _is_keyF11;
05578     volatile bool _is_keyF12;
05579     volatile bool _is_keyPAUSE;
05580     volatile bool _is_key1;
05581     volatile bool _is_key2;
05582     volatile bool _is_key3;
05583     volatile bool _is_key4;
05584     volatile bool _is_key5;
05585     volatile bool _is_key6;
05586     volatile bool _is_key7;
05587     volatile bool _is_key8;
05588     volatile bool _is_key9;
05589     volatile bool _is_key0;
05590     volatile bool _is_keyBACKSPACE;
05591     volatile bool _is_keyINSERT;
05592     volatile bool _is_keyHOME;
05593     volatile bool _is_keyPAGEUP;
05594     volatile bool _is_keyTAB;
05595     volatile bool _is_keyQ;
05596     volatile bool _is_keyW;
05597     volatile bool _is_keyE;
05598     volatile bool _is_keyR;
05599     volatile bool _is_keyT;
05600     volatile bool _is_keyY;
05601     volatile bool _is_keyU;
05602     volatile bool _is_keyI;
05603     volatile bool _is_keyO;
05604     volatile bool _is_keyP;
05605     volatile bool _is_keyDELETE;
05606     volatile bool _is_keyEND;
05607     volatile bool _is_keyPAGEDOWN;
05608     volatile bool _is_keyCAPSLOCK;
05609     volatile bool _is_keyA;
05610     volatile bool _is_keyS;
05611     volatile bool _is_keyD;
05612     volatile bool _is_keyF;
05613     volatile bool _is_keyG;
05614     volatile bool _is_keyH;
05615     volatile bool _is_keyJ;
05616     volatile bool _is_keyK;
05617     volatile bool _is_keyL;
05618     volatile bool _is_keyENTER;
05619     volatile bool _is_keySHIFTLEFT;
05620     volatile bool _is_keyZ;
05621     volatile bool _is_keyX;
05622     volatile bool _is_keyC;
05623     volatile bool _is_keyV;
05624     volatile bool _is_keyB;
05625     volatile bool _is_keyN;
05626     volatile bool _is_keyM;
05627     volatile bool _is_keySHIFTRIGHT;
05628     volatile bool _is_keyARROWUP;
05629     volatile bool _is_keyCTRLLEFT;
05630     volatile bool _is_keyAPPLEFT;
05631     volatile bool _is_keyALT;
05632     volatile bool _is_keySPACE;
05633     volatile bool _is_keyALTGR;
05634     volatile bool _is_keyAPPRIGHT;
05635     volatile bool _is_keyMENU;
05636     volatile bool _is_keyCTRLRIGHT;
05637     volatile bool _is_keyARROWLEFT;
05638     volatile bool _is_keyARROWDOWN;
05639     volatile bool _is_keyARROWRIGHT;
05640     volatile bool _is_keyPAD0;
05641     volatile bool _is_keyPAD1;
05642     volatile bool _is_keyPAD2;
05643     volatile bool _is_keyPAD3;
05644     volatile bool _is_keyPAD4;
05645     volatile bool _is_keyPAD5;
05646     volatile bool _is_keyPAD6;
05647     volatile bool _is_keyPAD7;
05648     volatile bool _is_keyPAD8;
05649     volatile bool _is_keyPAD9;
05650     volatile bool _is_keyPADADD;
05651     volatile bool _is_keyPADSUB;
05652     volatile bool _is_keyPADMUL;
05653     volatile bool _is_keyPADDIV;
05654 
05655     //! Fullscreen state of the display.
05656     bool _is_fullscreen;
05657 
05658     // Internal variables.
05659     float _fps_fps, _min, _max;
05660     unsigned long _timer, _fps_frames, _fps_timer;
05661 
05662     //@}
05663     //---------------------------
05664     //
05665     //! \name Plugins
05666     //@{
05667     //---------------------------
05668 
05669 #ifdef cimgdisplay_plugin
05670 #include cimgdisplay_plugin
05671 #endif
05672 #ifdef cimgdisplay_plugin1
05673 #include cimgdisplay_plugin1
05674 #endif
05675 #ifdef cimgdisplay_plugin2
05676 #include cimgdisplay_plugin2
05677 #endif
05678 #ifdef cimgdisplay_plugin3
05679 #include cimgdisplay_plugin3
05680 #endif
05681 #ifdef cimgdisplay_plugin4
05682 #include cimgdisplay_plugin4
05683 #endif
05684 #ifdef cimgdisplay_plugin5
05685 #include cimgdisplay_plugin5
05686 #endif
05687 #ifdef cimgdisplay_plugin6
05688 #include cimgdisplay_plugin6
05689 #endif
05690 #ifdef cimgdisplay_plugin7
05691 #include cimgdisplay_plugin7
05692 #endif
05693 #ifdef cimgdisplay_plugin8
05694 #include cimgdisplay_plugin8
05695 #endif
05696 
05697     //@}
05698     //--------------------------------------------------------
05699     //
05700     //! \name Constructors / Destructor / Instance Management
05701     //@{
05702     //--------------------------------------------------------
05703 
05704     //! Destructor.
05705     ~CImgDisplay() {
05706       assign();
05707     }
05708 
05709     //! Create an empty display window.
05710     CImgDisplay():
05711       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
05712       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
05713       _is_fullscreen(false),_min(0),_max(0) {
05714       assign();
05715     }
05716 
05717     //! Create a display window with a specified size \p pwidth x \p height.
05718     /** \param width Width of the display window.
05719         \param height Height of the display window.
05720         \param title Title of the display window.
05721         \param normalization Normalization type of the display window (0=none, 1=always, 2=once).
05722         \param is_fullscreen : Fullscreen mode.
05723         \param is_closed : Initially visible mode.
05724         A black image will be initially displayed in the display window.
05725     **/
05726     CImgDisplay(const unsigned int width, const unsigned int height,
05727                 const char *const title=0, const unsigned int normalization=3,
05728                 const bool is_fullscreen=false, const bool is_closed=false):
05729       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
05730       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
05731       _is_fullscreen(false),_min(0),_max(0) {
05732       assign(width,height,title,normalization,is_fullscreen,is_closed);
05733     }
05734 
05735     //! Create a display window from an image.
05736     /** \param img : Image that will be used to create the display window.
05737         \param title : Title of the display window
05738         \param normalization : Normalization type of the display window.
05739         \param is_fullscreen : Fullscreen mode.
05740         \param is_closed : Initially visible mode.
05741     **/
05742     template<typename T>
05743     explicit CImgDisplay(const CImg<T>& img,
05744                          const char *const title=0, const unsigned int normalization=3,
05745                          const bool is_fullscreen=false, const bool is_closed=false):
05746       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
05747       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
05748       _is_fullscreen(false),_min(0),_max(0) {
05749       assign(img,title,normalization,is_fullscreen,is_closed);
05750     }
05751 
05752     //! Create a display window from an image list.
05753     /** \param list : The list of images to display.
05754         \param title : Title of the display window
05755         \param normalization : Normalization type of the display window.
05756         \param is_fullscreen : Fullscreen mode.
05757         \param is_closed : Initially visible mode.
05758     **/
05759     template<typename T>
05760     explicit CImgDisplay(const CImgList<T>& list,
05761                          const char *const title=0, const unsigned int normalization=3,
05762                          const bool is_fullscreen=false, const bool is_closed=false):
05763       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
05764       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
05765       _is_fullscreen(false),_min(0),_max(0) {
05766       assign(list,title,normalization,is_fullscreen,is_closed);
05767     }
05768 
05769     //! Create a display window by copying another one.
05770     /**
05771         \param disp  : Display window to copy.
05772     **/
05773     CImgDisplay(const CImgDisplay& disp):
05774       _width(0),_height(0),_window_width(0),_window_height(0),_window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),
05775       _normalization(0),_title(0),_button(0),_wheel(0),_is_closed(true),_is_resized(false),_is_moved(false),_is_event(false),
05776       _is_fullscreen(false),_min(0),_max(0) {
05777       assign(disp);
05778     }
05779 
05780 #if cimg_display==0
05781 
05782     static void _no_display_exception() {
05783       throw CImgDisplayException("CImgDisplay() : No display available.");
05784     }
05785 
05786     //! In-place version of the destructor.
05787     CImgDisplay& assign() {
05788       _no_display_exception();
05789       return flush();
05790     }
05791 
05792     //! In-place version of the constructor.
05793     CImgDisplay& assign(const unsigned int width, const unsigned int height,
05794                         const char *const title=0, const unsigned int normalization=3,
05795                         const bool is_fullscreen=false, const bool is_closed=false) {
05796       cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
05797       return assign();
05798     }
05799 
05800     //! In-place version of the constructor.
05801     template<typename T>
05802     CImgDisplay& assign(const CImg<T>& img,
05803                         const char *const title=0, const unsigned int normalization=3,
05804                         const bool is_fullscreen=false, const bool is_closed=false) {
05805       return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
05806     }
05807 
05808     //! In-place version of the constructor.
05809     template<typename T>
05810     CImgDisplay& assign(const CImgList<T>& list,
05811                         const char *const title=0, const unsigned int normalization=3,
05812                         const bool is_fullscreen=false, const bool is_closed=false) {
05813       return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
05814     }
05815 
05816     //! In-place version of the constructor.
05817     CImgDisplay& assign(const CImgDisplay &disp) {
05818       return assign(disp._width,disp._height);
05819     }
05820 
05821 #endif
05822 
05823     //! Return a reference to an empty display.
05824     static CImgDisplay& empty() {
05825       static CImgDisplay _empty;
05826       return _empty.assign();
05827     }
05828 
05829 #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false),CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true)
05830     static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1,
05831                                    const int dmin=128, const int dmax=-85,const bool return_last=false) {
05832       unsigned int nw = dx + (dz>1?dz:0), nh = dy + (dz>1?dz:0);
05833       const unsigned int
05834         sw = CImgDisplay::screen_width(), sh = CImgDisplay::screen_height(),
05835         mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin,
05836         mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin,
05837         Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax,
05838         Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax;
05839       if (nw<mw) { nh = nh*mw/nw; nh+=(nh==0?1:0); nw = mw; }
05840       if (nh<mh) { nw = nw*mh/nh; nw+=(nw==0?1:0); nh = mh; }
05841       if (nw>Mw) { nh = nh*Mw/nw; nh+=(nh==0?1:0); nw = Mw; }
05842       if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0?1:0); nh = Mh; }
05843       if (nw<mw) nw = mw;
05844       if (nh<mh) nh = mh;
05845       if (return_last) return nh;
05846       return nw;
05847     }
05848 
05849     //@}
05850     //------------------------------------------
05851     //
05852     //! \name Overloaded Operators
05853     //@{
05854     //------------------------------------------
05855 
05856     // Operator=().
05857     template<typename t>
05858     CImgDisplay& operator=(const CImg<t>& img) {
05859       return display(img);
05860     }
05861 
05862     // Operator=().
05863     template<typename t>
05864     CImgDisplay& operator=(const CImgList<t>& list) {
05865       return display(list);
05866     }
05867 
05868     //! Operator=().
05869     CImgDisplay& operator=(const CImgDisplay& disp) {
05870       return assign(disp);
05871     }
05872 
05873     //! Return true if display is not empty.
05874     operator bool() const {
05875       return !is_empty();
05876     }
05877 
05878     //@}
05879     //------------------------------------------
05880     //
05881     //! \name Instance Checking
05882     //@{
05883     //------------------------------------------
05884 
05885     //! Return true is display is empty.
05886     bool is_empty() const {
05887       return !(_width && _height);
05888     }
05889 
05890     bool is_closed() const {
05891       return _is_closed;
05892     }
05893 
05894     bool is_resized() const {
05895       return _is_resized;
05896     }
05897 
05898     bool is_moved() const {
05899       return _is_moved;
05900     }
05901 
05902     bool is_event() const {
05903       return _is_event;
05904     }
05905 
05906     bool is_fullscreen() const {
05907       return _is_fullscreen;
05908     }
05909 
05910     bool is_key() const {
05911       return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
05912         _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
05913         _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
05914         _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
05915         _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
05916         _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
05917         _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
05918         _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
05919         _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
05920         _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
05921         _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
05922         _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
05923         _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
05924         _is_keyK || _is_keyL || _is_keyENTER ||
05925         _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
05926         _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
05927         _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
05928         _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
05929         _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
05930         _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
05931         _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
05932         _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
05933         _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
05934         _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
05935         _is_keyPADMUL || _is_keyPADDIV;
05936     }
05937 
05938 #define _cimg_iskey_def(k) \
05939     bool is_key##k() const { \
05940       return _is_key##k; \
05941     }
05942     _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
05943     _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
05944     _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
05945     _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
05946     _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
05947     _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
05948     _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
05949     _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
05950     _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
05951     _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
05952     _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
05953     _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
05954     _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
05955     _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
05956     _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
05957     _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
05958     _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
05959     _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
05960     _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
05961     _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
05962     _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
05963     _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
05964     _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
05965     _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
05966     _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
05967 
05968     bool is_key(const unsigned int key) const {
05969 #define _cimg_iskey_test(k) if (key==cimg::key##k) return _is_key##k;
05970       _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
05971       _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
05972       _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
05973       _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
05974       _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
05975       _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
05976       _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
05977       _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
05978       _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
05979       _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
05980       _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
05981       _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
05982       _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
05983       _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
05984       _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
05985       _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
05986       _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
05987       _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
05988       _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
05989       _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
05990       _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
05991       _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
05992       _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
05993       _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
05994       _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
05995       return false;
05996     }
05997 
05998     //! Get keycode corresponding to given input string.
05999     bool is_key(const char *const textcode) const {
06000 #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(textcode,#k)) return _is_key##k;
06001       _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
06002       _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
06003       _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
06004       _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
06005       _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
06006       _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
06007       _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
06008       _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
06009       _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
06010       _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
06011       _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
06012       _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
06013       _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
06014       _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
06015       _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
06016       _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
06017       _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
06018       _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
06019       _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
06020       _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
06021       _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
06022       _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
06023       _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
06024       _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
06025       _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
06026       return false;
06027     }
06028 
06029     //! Test if a key sequence has been typed.
06030     bool is_key_sequence(const unsigned int *const key_sequence, const unsigned int length, const bool remove_sequence=false) {
06031       if (key_sequence && length) {
06032         const unsigned int
06033           *const ps_end = key_sequence + length - 1,
06034           *const pk_end = (unsigned int*)_keys + 1 + sizeof(_keys)/sizeof(unsigned int) - length,
06035           k = *ps_end;
06036         for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
06037           if (*(pk++)==k) {
06038             bool res = true;
06039             const unsigned int *ps = ps_end, *pk2 = pk;
06040             for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
06041             if (res) {
06042               if (remove_sequence) std::memset((void*)(pk-1),0,sizeof(unsigned int)*length);
06043               return true;
06044             }
06045           }
06046         }
06047       }
06048       return false;
06049     }
06050 
06051     //@}
06052     //------------------------------------------
06053     //
06054     //! \name Instance Characteristics
06055     //@{
06056     //------------------------------------------
06057 
06058     //! Return display width.
06059     int width() const {
06060       return (int)_width;
06061     }
06062 
06063     //! Return display height.
06064     int height() const {
06065       return (int)_height;
06066     }
06067 
06068     //! Return X-coordinate of the mouse pointer.
06069     int mouse_x() const {
06070       return _mouse_x;
06071     }
06072 
06073     //! Return Y-coordinate of the mouse pointer.
06074     int mouse_y() const {
06075       return _mouse_y;
06076     }
06077 
06078     //! Return current or previous state of the mouse buttons.
06079     unsigned int button() const {
06080       return _button;
06081     }
06082 
06083     //! Return current state of the mouse wheel.
06084     int wheel() const {
06085       return _wheel;
06086     }
06087 
06088     //! Return current or previous state of the keyboard.
06089     unsigned int key(const unsigned int pos=0) const {
06090       return pos<(sizeof(_keys)/sizeof(unsigned int))?_keys[pos]:0;
06091     }
06092 
06093     unsigned int released_key(const unsigned int pos=0) const {
06094       return pos<(sizeof(_released_keys)/sizeof(unsigned int))?_released_keys[pos]:0;
06095     }
06096 
06097     //! Get keycode corresponding to given input string.
06098     static unsigned int keycode(const char *const textcode) {
06099 #define _cimg_keycode(k) if (!cimg::strcasecmp(textcode,#k)) return cimg::key##k;
06100       _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
06101       _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
06102       _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
06103       _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
06104       _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
06105       _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
06106       _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
06107       _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
06108       _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
06109       _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
06110       _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
06111       _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
06112       _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
06113       _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
06114       _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
06115       _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
06116       _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
06117       _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
06118       _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
06119       _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
06120       _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
06121       _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
06122       _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
06123       _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
06124       _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
06125       return 0;
06126     }
06127 
06128     //! Return normalization type of the display.
06129     unsigned int normalization() const {
06130       return _normalization;
06131     }
06132 
06133     //! Return title of the display.
06134     const char *title() const {
06135       return _title;
06136     }
06137 
06138     //! Return display window width.
06139     int window_width() const {
06140       return (int)_window_width;
06141     }
06142 
06143     //! Return display window height.
06144     int window_height() const {
06145       return (int)_window_height;
06146     }
06147 
06148     //! Return X-coordinate of the window.
06149     int window_x() const {
06150       return _window_x;
06151     }
06152 
06153     //! Return Y-coordinate of the window.
06154     int window_y() const {
06155       return _window_y;
06156     }
06157 
06158 #if cimg_display==0
06159 
06160     //! Return the width of the screen resolution.
06161     static int screen_width() {
06162       _no_display_exception();
06163       return 0;
06164     }
06165 
06166     //! Return the height of the screen resolution.
06167     static int screen_height() {
06168       _no_display_exception();
06169       return 0;
06170     }
06171 
06172 #endif
06173 
06174     //! Return the frame per second rate.
06175     float frames_per_second() {
06176       if (!_fps_timer) _fps_timer = cimg::time();
06177       const float delta = (cimg::time()-_fps_timer)/1000.0f;
06178       ++_fps_frames;
06179       if (delta>=1) {
06180         _fps_fps = _fps_frames/delta;
06181         _fps_frames = 0;
06182         _fps_timer = cimg::time();
06183       }
06184       return _fps_fps;
06185     }
06186 
06187     //@}
06188     //------------------------------------------
06189     //
06190     //! \name Display Manipulation
06191     //@{
06192     //------------------------------------------
06193 
06194 #if cimg_display==0
06195 
06196     //! Display an image in a window.
06197     template<typename T>
06198     CImgDisplay& display(const CImg<T>& img) {
06199       return assign(img);
06200     }
06201 
06202 #endif
06203 
06204     //! Display an image list CImgList<T> into a display window.
06205     /** First, all images of the list are appended into a single image used for visualization,
06206         then this image is displayed in the current display window.
06207         \param list     : The list of images to display.
06208         \param axis     : The axis used to append the image for visualization. Can be 'x' (default),'y','z' or 'c'.
06209         \param align : Defines the relative alignment of images when displaying images of different sizes.
06210         Can be '\p c' (centered, which is the default), '\p p' (top alignment) and '\p n' (bottom aligment).
06211     **/
06212     template<typename T>
06213     CImgDisplay& display(const CImgList<T>& list, const char axis='x', const char align='p') {
06214       return display(list.get_append(axis,align));
06215     }
06216 
06217     //! Resize a display window in its current size.
06218     CImgDisplay& resize(const bool redraw=true) {
06219       resize(_window_width,_window_height,redraw);
06220       return *this;
06221     }
06222 
06223     //! Resize a display window with the size of an image.
06224     /** \param img    : Input image. \p image.width and \p image.height give the new dimensions of the display window.
06225         \param redraw : If \p true (default), the current displayed image in the display window will
06226         be bloc-interpolated to fit the new dimensions. If \p false, a black image will be drawn in the resized window.
06227     **/
06228     template<typename T>
06229     CImgDisplay& resize(const CImg<T>& img, const bool redraw=true) {
06230       return resize(img._width,img._height,redraw);
06231     }
06232 
06233     //! Resize a display window using the size of the given display \p disp.
06234     CImgDisplay& resize(const CImgDisplay& disp, const bool redraw=true) {
06235       return resize(disp._width,disp._height,redraw);
06236     }
06237 
06238 #if cimg_display==0
06239 
06240     //! Resize window.
06241     CImgDisplay& resize(const int width, const int height, const bool redraw=true) {
06242       return assign(width,height,0,3,redraw);
06243     }
06244 
06245 #endif
06246 
06247     // Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
06248     template<typename t, typename T>
06249     static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
06250                                t *ptrd, const unsigned int wd, const unsigned int hd) {
06251       unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy;
06252       float s, curr, old;
06253       s = (float)ws/wd;
06254       poffx = offx; curr = 0; for (unsigned int x = 0; x<wd; ++x) { old = curr; curr+=s; *(poffx++) = (unsigned int)curr - (unsigned int)old; }
06255       s = (float)hs/hd;
06256       poffy = offy; curr = 0; for (unsigned int y = 0; y<hd; ++y) { old = curr; curr+=s; *(poffy++) = ws*((unsigned int)curr - (unsigned int)old); }
06257       *poffy = 0;
06258       poffy = offy;
06259       for (unsigned int y = 0; y<hd; ) {
06260         const T *ptr = ptrs;
06261         poffx = offx;
06262         for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
06263         ++y;
06264         unsigned int dy = *(poffy++);
06265         for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poffy++)) {}
06266         ptrs+=dy;
06267       }
06268       delete[] offx; delete[] offy;
06269     }
06270 
06271     //! Set fullscreen mode.
06272     CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool redraw=true) {
06273       if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
06274       return toggle_fullscreen(redraw);
06275     }
06276 
06277 #if cimg_display==0
06278 
06279     //! Toggle fullscreen mode.
06280     CImgDisplay& toggle_fullscreen(const bool redraw=true) {
06281       return assign(_width,_height,0,3,redraw);
06282     }
06283 
06284     //! Show a closed display.
06285     CImgDisplay& show() {
06286       return assign();
06287     }
06288 
06289     //! Close a visible display.
06290     CImgDisplay& close() {
06291       return assign();
06292     }
06293 
06294     //! Move window.
06295     CImgDisplay& move(const int pos_x, const int pos_y) {
06296       return assign(pos_x,pos_y);
06297     }
06298 
06299     //! Show mouse pointer.
06300     CImgDisplay& show_mouse() {
06301       return assign();
06302     }
06303 
06304     //! Hide mouse pointer.
06305     CImgDisplay& hide_mouse() {
06306       return assign();
06307     }
06308 
06309     //! Move mouse pointer to a specific location.
06310     CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
06311       return assign(pos_x,pos_y);
06312     }
06313 
06314     //! Set the window title.
06315     CImgDisplay& set_title(const char *const format, ...) {
06316       return assign(0,0,format);
06317     }
06318 
06319     //! Render image buffer into GDI native image format.
06320     template<typename T>
06321     CImgDisplay& render(const CImg<T>& img) {
06322       return assign(img);
06323     }
06324 
06325     //! Re-paint image content in window.
06326     CImgDisplay& paint() {
06327       return assign();
06328     }
06329 
06330     //! Take a snapshot of the display in the specified image.
06331     template<typename T>
06332     const CImgDisplay& snapshot(CImg<T>& img) const {
06333       _no_display_exception();
06334       return *this;
06335     }
06336 #endif
06337 
06338     //! Simulate a mouse button event.
06339     CImgDisplay& set_button() {
06340       _button = 0;
06341       _is_event = true;
06342       return *this;
06343     }
06344 
06345     CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
06346       const unsigned int buttoncode = button==1?1:button==2?2:button==3?4:0;
06347       if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
06348       _is_event = buttoncode?true:false;
06349       return *this;
06350     }
06351 
06352     //! Simulate a mouse wheel event, or flush wheel events.
06353     CImgDisplay& set_wheel() {
06354       _wheel = 0;
06355       _is_event = true;
06356       return *this;
06357     }
06358 
06359     CImgDisplay& set_wheel(const int amplitude) {
06360       _wheel+=amplitude;
06361       _is_event = amplitude?true:false;
06362       return *this;
06363     }
06364 
06365     //! Simulate a keyboard press/release event, or flush all key events.
06366     CImgDisplay& set_key() {
06367       std::memset((void*)_keys,0,sizeof(_keys));
06368       std::memset((void*)_released_keys,0,sizeof(_released_keys));
06369       _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 = _is_keyF9 =
06370         _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 = _is_key5 = _is_key6 =
06371         _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT = _is_keyHOME = _is_keyPAGEUP = _is_keyTAB =
06372         _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY = _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE =
06373         _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK = _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ =
06374         _is_keyK = _is_keyL = _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
06375         _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE = _is_keyALTGR = _is_keyAPPRIGHT =
06376         _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN = _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 =
06377         _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 = _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB =
06378         _is_keyPADMUL = _is_keyPADDIV = false;
06379       _is_event = true;
06380       return *this;
06381     }
06382 
06383     CImgDisplay& set_key(const unsigned int keycode, const bool pressed=true) {
06384 #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = pressed;
06385       _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
06386       _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
06387       _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
06388       _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
06389       _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
06390       _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
06391       _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
06392       _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
06393       _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
06394       _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
06395       _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
06396       _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
06397       _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
06398       _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
06399       _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
06400       _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
06401       _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
06402       _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
06403       _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
06404       _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
06405       _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
06406       _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
06407       _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
06408       _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
06409       _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
06410       if (pressed) {
06411         if (*_keys)
06412           std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
06413         *_keys = keycode;
06414         if (*_released_keys) {
06415           std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
06416           *_released_keys = 0;
06417         }
06418       } else {
06419         if (*_keys) {
06420           std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
06421           *_keys = 0;
06422         }
06423         if (*_released_keys)
06424           std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
06425         *_released_keys = keycode;
06426       }
06427       _is_event = keycode?true:false;
06428       return *this;
06429     }
06430 
06431     //! Flush all display events.
06432     CImgDisplay& flush() {
06433       set_key().set_button().set_wheel();
06434       _is_resized = _is_moved = _is_event = false;
06435       _fps_timer = _fps_frames = _timer = 0;
06436       _fps_fps = 0;
06437       return *this;
06438     }
06439 
06440     //! Synchronized waiting function. Same as cimg::wait().
06441     CImgDisplay& wait(const unsigned int milliseconds) {
06442       cimg::_sleep(milliseconds,_timer);
06443       return *this;
06444     }
06445 
06446     //! Wait for an event occuring on the current display.
06447     CImgDisplay& wait() {
06448       if (!is_empty()) wait(*this);
06449       return *this;
06450     }
06451 
06452     //! Wait for any event occuring on the display \c disp1.
06453     static void wait(CImgDisplay& disp1) {
06454       disp1._is_event = 0;
06455       while (!disp1._is_event) wait_all();
06456     }
06457 
06458     //! Wait for any event occuring either on the display \c disp1 or \c disp2.
06459     static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
06460       disp1._is_event = disp2._is_event = 0;
06461       while (!disp1._is_event && !disp2._is_event) wait_all();
06462     }
06463 
06464     //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3.
06465     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
06466       disp1._is_event = disp2._is_event = disp3._is_event = 0;
06467       while (!disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
06468     }
06469 
06470     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
06471     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
06472       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = 0;
06473       while (!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
06474     }
06475 
06476     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
06477     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5) {
06478       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = 0;
06479       while (!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event) wait_all();
06480     }
06481 
06482     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
06483     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
06484                      CImgDisplay& disp6) {
06485       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
06486         disp6._is_event = 0;
06487       while (!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
06488              !disp6._is_event) wait_all();
06489     }
06490 
06491     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
06492     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
06493                      CImgDisplay& disp6, CImgDisplay& disp7) {
06494       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
06495         disp6._is_event = disp7._is_event = 0;
06496       while (!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
06497              !disp6._is_event && !disp7._is_event) wait_all();
06498     }
06499 
06500     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
06501     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
06502                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
06503       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
06504         disp6._is_event = disp7._is_event = disp8._is_event = 0;
06505       while (!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
06506              !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
06507     }
06508 
06509     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
06510     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
06511                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
06512       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
06513         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = 0;
06514       while (!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
06515              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
06516     }
06517 
06518     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
06519     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
06520                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9, CImgDisplay& disp10) {
06521       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
06522         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = 0;
06523       while (!disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
06524              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) wait_all();
06525     }
06526 
06527 #if cimg_display==0
06528 
06529     //! Wait for a window event in any CImg window.
06530     static void wait_all() {
06531       return _no_display_exception();
06532     }
06533 
06534 #endif
06535 
06536     // X11-based implementation
06537     //--------------------------
06538 #if cimg_display==1
06539 
06540     Atom _wm_window_atom, _wm_protocol_atom;
06541     Window _window, _background_window;
06542     Colormap _colormap;
06543     XImage *_image;
06544     void *_data;
06545 #ifdef cimg_use_xshm
06546     XShmSegmentInfo *_shminfo;
06547 #endif
06548 
06549     static int screen_width() {
06550       int res = 0;
06551       if (!cimg::X11_attr().display) {
06552         Display *disp = XOpenDisplay((std::getenv("DISPLAY")?std::getenv("DISPLAY"):":0.0"));
06553         if (!disp)
06554           throw CImgDisplayException("CImgDisplay::screen_width() : Failed to open X11 display.");
06555         res = DisplayWidth(disp,DefaultScreen(disp));
06556         XCloseDisplay(disp);
06557       } else {
06558 #ifdef cimg_use_xrandr
06559         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
06560           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
06561         else
06562 #endif
06563           res = DisplayWidth(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display));
06564       }
06565       return res;
06566     }
06567 
06568     static int screen_height() {
06569       int res = 0;
06570       if (!cimg::X11_attr().display) {
06571         Display *disp = XOpenDisplay((std::getenv("DISPLAY") ? std::getenv("DISPLAY") : ":0.0"));
06572         if (!disp)
06573           throw CImgDisplayException("CImgDisplay::screen_height() : Failed to open X11 display.");
06574         res = DisplayHeight(disp,DefaultScreen(disp));
06575         XCloseDisplay(disp);
06576       } else {
06577 #ifdef cimg_use_xrandr
06578         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
06579           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
06580         else
06581 #endif
06582           res = DisplayHeight(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display));
06583       }
06584       return res;
06585     }
06586 
06587     static void wait_all() {
06588       if (cimg::X11_attr().display) {
06589         XLockDisplay(cimg::X11_attr().display);
06590         bool flag = true;
06591         XEvent event;
06592         while (flag) {
06593           XNextEvent(cimg::X11_attr().display, &event);
06594           for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
06595             if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) {
06596               cimg::X11_attr().wins[i]->_handle_events(&event);
06597               if (cimg::X11_attr().wins[i]->_is_event) flag = false;
06598             }
06599         }
06600         XUnlockDisplay(cimg::X11_attr().display);
06601       }
06602     }
06603 
06604     void _handle_events(const XEvent *const pevent) {
06605       XEvent event = *pevent;
06606       switch (event.type) {
06607       case ClientMessage : {
06608         if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
06609             (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
06610           XUnmapWindow(cimg::X11_attr().display,_window);
06611           _is_closed = _is_event = true;
06612         }
06613       } break;
06614       case ConfigureNotify : {
06615         while (XCheckWindowEvent(cimg::X11_attr().display,_window,StructureNotifyMask,&event)) {}
06616         const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
06617         const int nx = event.xconfigure.x, ny = event.xconfigure.y;
06618         if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
06619           _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
06620           XResizeWindow(cimg::X11_attr().display,_window,_window_width,_window_height);
06621           _is_resized = _is_event = true;
06622         }
06623         if (nx!=_window_x || ny!=_window_y) { _window_x = nx; _window_y = ny; _is_moved = _is_event = true; }
06624       } break;
06625       case Expose : {
06626         while (XCheckWindowEvent(cimg::X11_attr().display,_window,ExposureMask,&event)) {}
06627         _paint(false);
06628         if (_is_fullscreen) {
06629           XWindowAttributes attr;
06630           XGetWindowAttributes(cimg::X11_attr().display,_window,&attr);
06631           while (attr.map_state!=IsViewable) XSync(cimg::X11_attr().display,False);
06632           XSetInputFocus(cimg::X11_attr().display,_window,RevertToParent,CurrentTime);
06633         }
06634       } break;
06635       case ButtonPress : {
06636         do {
06637           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
06638           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
06639           switch (event.xbutton.button) {
06640           case 1 : set_button(1); break;
06641           case 3 : set_button(2); break;
06642           case 2 : set_button(3); break;
06643           }
06644         } while (XCheckWindowEvent(cimg::X11_attr().display,_window,ButtonPressMask,&event));
06645       } break;
06646       case ButtonRelease : {
06647         do {
06648           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
06649           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
06650           switch (event.xbutton.button) {
06651           case 1 : set_button(1,false); break;
06652           case 3 : set_button(2,false); break;
06653           case 2 : set_button(3,false); break;
06654           case 4 : set_wheel(1); break;
06655           case 5 : set_wheel(-1); break;
06656           }
06657         } while (XCheckWindowEvent(cimg::X11_attr().display,_window,ButtonReleaseMask,&event));
06658       } break;
06659       case KeyPress : {
06660         char tmp = 0; KeySym ksym;
06661         XLookupString(&event.xkey,&tmp,1,&ksym,0);
06662         set_key((unsigned int)ksym,true);
06663       } break;
06664       case KeyRelease : {
06665         char tmp = 0; KeySym ksym;
06666         XLookupString(&event.xkey,&tmp,1,&ksym,0);
06667         set_key((unsigned int)ksym,false);
06668       } break;
06669       case EnterNotify: {
06670         while (XCheckWindowEvent(cimg::X11_attr().display,_window,EnterWindowMask,&event)) {}
06671         _mouse_x = event.xmotion.x;
06672         _mouse_y = event.xmotion.y;
06673         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
06674       } break;
06675       case LeaveNotify : {
06676         while (XCheckWindowEvent(cimg::X11_attr().display,_window,LeaveWindowMask,&event)) {}
06677         _mouse_x = _mouse_y =-1; _is_event = true;
06678       } break;
06679       case MotionNotify : {
06680         while (XCheckWindowEvent(cimg::X11_attr().display,_window,PointerMotionMask,&event)) {}
06681         _mouse_x = event.xmotion.x;
06682         _mouse_y = event.xmotion.y;
06683         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
06684         _is_event = true;
06685       } break;
06686       }
06687     }
06688 
06689     static void* _events_thread(void *arg) {
06690       arg = 0;
06691       XEvent event;
06692       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
06693       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
06694       for (;;) {
06695         XLockDisplay(cimg::X11_attr().display);
06696         bool event_flag = XCheckTypedEvent(cimg::X11_attr().display,ClientMessage,&event);
06697         if (!event_flag) event_flag = XCheckMaskEvent(cimg::X11_attr().display,
06698                                                       ExposureMask | StructureNotifyMask | ButtonPressMask|
06699                                                       KeyPressMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask|
06700                                                       ButtonReleaseMask | KeyReleaseMask,&event);
06701         if (event_flag) for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
06702           if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
06703             cimg::X11_attr().wins[i]->_handle_events(&event);
06704         XUnlockDisplay(cimg::X11_attr().display);
06705         pthread_testcancel();
06706         cimg::sleep(8);
06707       }
06708       return 0;
06709     }
06710 
06711     void _set_colormap(Colormap& colormap, const unsigned int dim) {
06712       XColor palette[256];
06713       switch (dim) {
06714       case 1 : { // palette for greyscale images
06715         for (unsigned int index = 0; index<256; ++index) {
06716           palette[index].pixel = index;
06717           palette[index].red = palette[index].green = palette[index].blue = (unsigned short)(index<<8);
06718           palette[index].flags = DoRed | DoGreen | DoBlue;
06719         }
06720       } break;
06721       case 2 : { // palette for RG images
06722         for (unsigned int index = 0, r = 8; r<256; r+=16)
06723           for (unsigned int g = 8; g<256; g+=16) {
06724             palette[index].pixel = index;
06725             palette[index].red = palette[index].blue = (unsigned short)(r<<8);
06726             palette[index].green = (unsigned short)(g<<8);
06727             palette[index++].flags = DoRed | DoGreen | DoBlue;
06728           }
06729       } break;
06730       default : { // palette for RGB images
06731         for (unsigned int index = 0, r = 16; r<256; r+=32)
06732           for (unsigned int g = 16; g<256; g+=32)
06733             for (unsigned int b = 32; b<256; b+=64) {
06734               palette[index].pixel = index;
06735               palette[index].red = (unsigned short)(r<<8);
06736               palette[index].green = (unsigned short)(g<<8);
06737               palette[index].blue = (unsigned short)(b<<8);
06738               palette[index++].flags = DoRed | DoGreen | DoBlue;
06739             }
06740       }
06741       }
06742       XStoreColors(cimg::X11_attr().display,colormap,palette,256);
06743     }
06744 
06745     void _map_window() {
06746       XWindowAttributes attr;
06747       XEvent event;
06748       bool exposed = false, mapped = false;
06749       XMapRaised(cimg::X11_attr().display,_window);
06750       XSync(cimg::X11_attr().display,False);
06751       do {
06752         XWindowEvent(cimg::X11_attr().display,_window,StructureNotifyMask | ExposureMask,&event);
06753         switch (event.type) {
06754         case MapNotify : mapped = true; break;
06755         case Expose : exposed = true; break;
06756         default : XSync(cimg::X11_attr().display, False); cimg::sleep(10);
06757         }
06758       } while (!(exposed && mapped));
06759       do {
06760         XGetWindowAttributes(cimg::X11_attr().display, _window, &attr);
06761         if (attr.map_state!=IsViewable) { XSync(cimg::X11_attr().display,False); cimg::sleep(10); }
06762       } while (attr.map_state != IsViewable);
06763       _window_x = attr.x;
06764       _window_y = attr.y;
06765     }
06766 
06767     void _paint(const bool wait_expose=true) {
06768       if (!_is_closed) {
06769         if (wait_expose) {
06770           static XEvent event;
06771           event.xexpose.type = Expose;
06772           event.xexpose.serial = 0;
06773           event.xexpose.send_event = True;
06774           event.xexpose.display = cimg::X11_attr().display;
06775           event.xexpose.window = _window;
06776           event.xexpose.x = 0;
06777           event.xexpose.y = 0;
06778           event.xexpose.width = width();
06779           event.xexpose.height = height();
06780           event.xexpose.count = 0;
06781           XSendEvent(cimg::X11_attr().display, _window, False, 0, &event);
06782         } else {
06783 #ifdef cimg_use_xshm
06784           if (_shminfo) XShmPutImage(cimg::X11_attr().display,_window,*cimg::X11_attr().gc,_image,0,0,0,0,_width,_height,False);
06785           else
06786 #endif
06787             XPutImage(cimg::X11_attr().display,_window,*cimg::X11_attr().gc,_image,0,0,0,0,_width,_height);
06788           XSync(cimg::X11_attr().display, False);
06789         }
06790       }
06791     }
06792 
06793     template<typename T>
06794     void _resize(T foo, const unsigned int ndimx, const unsigned int ndimy, const bool redraw) {
06795       foo = 0;
06796 #ifdef cimg_use_xshm
06797       if (_shminfo) {
06798         XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
06799         XImage *const nimage = XShmCreateImage(cimg::X11_attr().display,DefaultVisual(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display)),
06800                                                cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
06801         if (!nimage) { delete nshminfo; return; }
06802         else {
06803           nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
06804           if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
06805           else {
06806             nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
06807             if (nshminfo->shmaddr==(char*)-1) { shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; }
06808             else {
06809               nshminfo->readOnly = False;
06810               cimg::X11_attr().shm_enabled = true;
06811               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
06812               XShmAttach(cimg::X11_attr().display, nshminfo);
06813               XSync(cimg::X11_attr().display, False);
06814               XSetErrorHandler(oldXErrorHandler);
06815               if (!cimg::X11_attr().shm_enabled) {
06816                 shmdt(nshminfo->shmaddr);
06817                 shmctl(nshminfo->shmid,IPC_RMID,0);
06818                 XDestroyImage(nimage);
06819                 delete nshminfo;
06820                 return;
06821               } else {
06822                 T *const ndata = (T*)nimage->data;
06823                 if (redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
06824                 else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
06825                 XShmDetach(cimg::X11_attr().display, _shminfo);
06826                 XDestroyImage(_image);
06827                 shmdt(_shminfo->shmaddr);
06828                 shmctl(_shminfo->shmid,IPC_RMID,0);
06829                 delete _shminfo;
06830                 _shminfo = nshminfo;
06831                 _image = nimage;
06832                 _data = (void*)ndata;
06833               }
06834             }
06835           }
06836         }
06837       } else
06838 #endif
06839         {
06840           T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
06841           if (redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
06842           else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
06843           _data = (void*)ndata;
06844           XDestroyImage(_image);
06845           _image = XCreateImage(cimg::X11_attr().display,DefaultVisual(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display)),
06846                                 cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
06847         }
06848     }
06849 
06850     void _init_fullscreen() {
06851       _background_window = 0;
06852       if (_is_fullscreen && !_is_closed) {
06853 #ifdef cimg_use_xrandr
06854         int foo;
06855         if (XRRQueryExtension(cimg::X11_attr().display,&foo,&foo)) {
06856           XRRRotations(cimg::X11_attr().display, DefaultScreen(cimg::X11_attr().display), &cimg::X11_attr().curr_rotation);
06857           if (!cimg::X11_attr().resolutions) {
06858             cimg::X11_attr().resolutions = XRRSizes(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display),&foo);
06859             cimg::X11_attr().nb_resolutions = (unsigned int)foo;
06860           }
06861           if (cimg::X11_attr().resolutions) {
06862             cimg::X11_attr().curr_resolution = 0;
06863             for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
06864               const unsigned int
06865                 nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
06866                 nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
06867               if (nw>=_width && nh>=_height &&
06868                   nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
06869                   nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
06870                 cimg::X11_attr().curr_resolution = i;
06871             }
06872             if (cimg::X11_attr().curr_resolution>0) {
06873               XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11_attr().display, DefaultRootWindow(cimg::X11_attr().display));
06874               XRRSetScreenConfig(cimg::X11_attr().display, config, DefaultRootWindow(cimg::X11_attr().display),
06875                                  cimg::X11_attr().curr_resolution, cimg::X11_attr().curr_rotation, CurrentTime);
06876               XRRFreeScreenConfigInfo(config);
06877               XSync(cimg::X11_attr().display, False);
06878             }
06879           }
06880         }
06881         if (!cimg::X11_attr().resolutions)
06882           cimg::warn(_cimgdisplay_instance
06883                      "_create_window() : Xrandr extension is not supported by the X server.",
06884                      cimgdisplay_instance);
06885 #endif
06886         const unsigned int sx = screen_width(), sy = screen_height();
06887         XSetWindowAttributes winattr;
06888         winattr.override_redirect = True;
06889         if (sx!=_width || sy!=_height) {
06890           _background_window = XCreateWindow(cimg::X11_attr().display,
06891                                              RootWindow(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display)),0,0,
06892                                              sx,sy,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
06893           const unsigned int bufsize = sx*sy*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
06894           void *background_data = std::malloc(bufsize);
06895           std::memset(background_data,0,bufsize);
06896           XImage *background_image = XCreateImage(cimg::X11_attr().display,DefaultVisual(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display)),
06897                                                   cimg::X11_attr().nb_bits,ZPixmap,0,(char*)background_data,sx,sy,8,0);
06898           XEvent event;
06899           XSelectInput(cimg::X11_attr().display,_background_window,StructureNotifyMask);
06900           XMapRaised(cimg::X11_attr().display,_background_window);
06901           do XWindowEvent(cimg::X11_attr().display,_background_window,StructureNotifyMask,&event);
06902           while (event.type!=MapNotify);
06903 #ifdef cimg_use_xshm
06904           if (_shminfo) XShmPutImage(cimg::X11_attr().display,_background_window,*cimg::X11_attr().gc,background_image,0,0,0,0,sx,sy,False);
06905           else
06906 #endif
06907             XPutImage(cimg::X11_attr().display,_background_window,*cimg::X11_attr().gc,background_image,0,0,0,0,sx,sy);
06908           XWindowAttributes attr;
06909           XGetWindowAttributes(cimg::X11_attr().display, _background_window, &attr);
06910           while (attr.map_state != IsViewable) XSync(cimg::X11_attr().display, False);
06911           XDestroyImage(background_image);
06912         }
06913       }
06914     }
06915 
06916     void _desinit_fullscreen() {
06917       if (_is_fullscreen) {
06918         XUngrabKeyboard(cimg::X11_attr().display,CurrentTime);
06919 #ifdef cimg_use_xrandr
06920         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
06921           XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11_attr().display, DefaultRootWindow(cimg::X11_attr().display));
06922           XRRSetScreenConfig(cimg::X11_attr().display, config, DefaultRootWindow(cimg::X11_attr().display),
06923                              0, cimg::X11_attr().curr_rotation, CurrentTime);
06924           XRRFreeScreenConfigInfo(config);
06925           XSync(cimg::X11_attr().display, False);
06926           cimg::X11_attr().curr_resolution = 0;
06927         }
06928 #endif
06929         if (_background_window) XDestroyWindow(cimg::X11_attr().display,_background_window);
06930         _background_window = 0;
06931         _is_fullscreen = false;
06932       }
06933     }
06934 
06935     static int _assign_xshm(Display *dpy, XErrorEvent *error) {
06936       dpy = 0; error = 0;
06937       cimg::X11_attr().shm_enabled = false;
06938       return 0;
06939     }
06940 
06941     void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
06942                  const unsigned int normalization_type=3,
06943                  const bool fullscreen_flag=false, const bool closed_flag=false) {
06944 
06945       // Allocate space for window title
06946       const char *const nptitle = ptitle?ptitle:"";
06947       const unsigned int s = std::strlen(nptitle) + 1;
06948       char *const tmp_title = s?new char[s]:0;
06949       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
06950 
06951       // Destroy previous display window if existing
06952       if (!is_empty()) assign();
06953 
06954       // Open X11 display if necessary.
06955       if (!cimg::X11_attr().display) {
06956         static bool xinit_threads = false;
06957         if (!xinit_threads) { XInitThreads(); xinit_threads = true; }
06958         cimg::X11_attr().nb_wins = 0;
06959         cimg::X11_attr().display = XOpenDisplay((std::getenv("DISPLAY")?std::getenv("DISPLAY"):":0.0"));
06960         if (!cimg::X11_attr().display)
06961           throw CImgDisplayException(_cimgdisplay_instance
06962                                      "assign() : Failed to open X11 display.",
06963                                      cimgdisplay_instance);
06964 
06965         cimg::X11_attr().nb_bits = DefaultDepth(cimg::X11_attr().display, DefaultScreen(cimg::X11_attr().display));
06966         if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 && cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
06967           throw CImgDisplayException(_cimgdisplay_instance
06968                                      "assign() : Invalid %u bits screen mode detected "
06969                                      "(only 8, 16, 24 and 32 bits modes are managed).",
06970                                      cimgdisplay_instance,
06971                                      cimg::X11_attr().nb_bits);
06972 
06973         cimg::X11_attr().gc = new GC;
06974         *cimg::X11_attr().gc = DefaultGC(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display));
06975         Visual *visual = DefaultVisual(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display));
06976         XVisualInfo vtemplate;
06977         vtemplate.visualid = XVisualIDFromVisual(visual);
06978         int nb_visuals;
06979         XVisualInfo *vinfo = XGetVisualInfo(cimg::X11_attr().display,VisualIDMask,&vtemplate,&nb_visuals);
06980         if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().blue_first = true;
06981         cimg::X11_attr().byte_order = ImageByteOrder(cimg::X11_attr().display);
06982         XFree(vinfo);
06983         XLockDisplay(cimg::X11_attr().display);
06984         cimg::X11_attr().event_thread = new pthread_t;
06985         pthread_create(cimg::X11_attr().event_thread,0,_events_thread,0);
06986       } else XLockDisplay(cimg::X11_attr().display);
06987 
06988       // Set display variables
06989       _width = cimg::min(dimw,(unsigned int)screen_width());
06990       _height = cimg::min(dimh,(unsigned int)screen_height());
06991       _normalization = normalization_type<4?normalization_type:3;
06992       _is_fullscreen = fullscreen_flag;
06993       _window_x = _window_y = 0;
06994       _is_closed = closed_flag;
06995       _title = tmp_title;
06996       flush();
06997 
06998       // Create X11 window (and LUT, if 8bits display)
06999       if (_is_fullscreen) {
07000         if (!_is_closed) _init_fullscreen();
07001         const unsigned int sx = screen_width(), sy = screen_height();
07002         XSetWindowAttributes winattr;
07003         winattr.override_redirect = True;
07004         _window = XCreateWindow(cimg::X11_attr().display,
07005                                 RootWindow(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display)),
07006                                 (sx-_width)/2,(sy-_height)/2,
07007                                 _width,_height,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
07008       } else
07009         _window = XCreateSimpleWindow(cimg::X11_attr().display,
07010                                      RootWindow(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display)),
07011                                      0,2,_width,_height,0,0,0x0L);
07012       XStoreName(cimg::X11_attr().display,_window,_title?_title:" ");
07013       if (cimg::X11_attr().nb_bits==8) {
07014         _colormap = XCreateColormap(cimg::X11_attr().display,_window,DefaultVisual(cimg::X11_attr().display,
07015                                                                                 DefaultScreen(cimg::X11_attr().display)),AllocAll);
07016         _set_colormap(_colormap,3);
07017         XSetWindowColormap(cimg::X11_attr().display,_window,_colormap);
07018       }
07019       _window_width = _width;
07020       _window_height = _height;
07021 
07022       // Create XImage
07023       const unsigned int bufsize = _width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
07024 #ifdef cimg_use_xshm
07025       _shminfo = 0;
07026       if (XShmQueryExtension(cimg::X11_attr().display)) {
07027         _shminfo = new XShmSegmentInfo;
07028         _image = XShmCreateImage(cimg::X11_attr().display,DefaultVisual(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display)),
07029                                 cimg::X11_attr().nb_bits,ZPixmap,0,_shminfo,_width,_height);
07030         if (!_image) {
07031           delete _shminfo;
07032           _shminfo = 0;
07033         } else {
07034           _shminfo->shmid = shmget(IPC_PRIVATE, bufsize, IPC_CREAT | 0777);
07035           if (_shminfo->shmid==-1) {
07036             XDestroyImage(_image);
07037             delete _shminfo;
07038             _shminfo = 0;
07039           } else {
07040             _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
07041             if (_shminfo->shmaddr==(char*)-1) {
07042               shmctl(_shminfo->shmid,IPC_RMID,0);
07043               XDestroyImage(_image);
07044               delete _shminfo;
07045               _shminfo = 0;
07046             } else {
07047               _shminfo->readOnly = False;
07048               cimg::X11_attr().shm_enabled = true;
07049               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
07050               XShmAttach(cimg::X11_attr().display, _shminfo);
07051               XSync(cimg::X11_attr().display, False);
07052               XSetErrorHandler(oldXErrorHandler);
07053               if (!cimg::X11_attr().shm_enabled) {
07054                 shmdt(_shminfo->shmaddr);
07055                 shmctl(_shminfo->shmid,IPC_RMID,0);
07056                 XDestroyImage(_image);
07057                 delete _shminfo;
07058                 _shminfo = 0;
07059               }
07060             }
07061           }
07062         }
07063       }
07064       if (!_shminfo)
07065 #endif
07066         {
07067           _data = std::malloc(bufsize);
07068           _image = XCreateImage(cimg::X11_attr().display,DefaultVisual(cimg::X11_attr().display,DefaultScreen(cimg::X11_attr().display)),
07069                                cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,_width,_height,8,0);
07070         }
07071 
07072       _wm_window_atom = XInternAtom(cimg::X11_attr().display,"WM_DELETE_WINDOW",False);
07073       _wm_protocol_atom = XInternAtom(cimg::X11_attr().display,"WM_PROTOCOLS",False);
07074       XSetWMProtocols(cimg::X11_attr().display,_window,&_wm_window_atom,1);
07075       XSelectInput(cimg::X11_attr().display,_window,
07076                    ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
07077                    EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
07078       if (_is_fullscreen) XGrabKeyboard(cimg::X11_attr().display, _window, True, GrabModeAsync, GrabModeAsync, CurrentTime);
07079       cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
07080       if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); }
07081       XUnlockDisplay(cimg::X11_attr().display);
07082     }
07083 
07084     CImgDisplay& assign() {
07085       if (is_empty()) return *this;
07086       XLockDisplay(cimg::X11_attr().display);
07087 
07088       // Remove display window from event thread list.
07089       unsigned int i;
07090       for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
07091       for (; i<cimg::X11_attr().nb_wins-1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i+1];
07092       --cimg::X11_attr().nb_wins;
07093 
07094       // Destroy window, image, colormap and title.
07095       if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
07096       XDestroyWindow(cimg::X11_attr().display,_window);
07097       _window = 0;
07098 #ifdef cimg_use_xshm
07099       if (_shminfo) {
07100         XShmDetach(cimg::X11_attr().display, _shminfo);
07101         XDestroyImage(_image);
07102         shmdt(_shminfo->shmaddr);
07103         shmctl(_shminfo->shmid,IPC_RMID,0);
07104         delete _shminfo;
07105         _shminfo = 0;
07106       } else
07107 #endif
07108         XDestroyImage(_image);
07109       _data = 0; _image = 0;
07110       if (cimg::X11_attr().nb_bits==8) XFreeColormap(cimg::X11_attr().display,_colormap);
07111       _colormap = 0;
07112       XSync(cimg::X11_attr().display, False);
07113 
07114       // Reset display variables
07115       if (_title) delete[] _title;
07116       _width = _height = _normalization = _window_width = _window_height = 0;
07117       _window_x = _window_y = 0;
07118       _is_fullscreen = false;
07119       _is_closed = true;
07120       _min = _max = 0;
07121       _title = 0;
07122       flush();
07123 
07124       // End event thread and close display if necessary
07125       XUnlockDisplay(cimg::X11_attr().display);
07126       if (!cimg::X11_attr().nb_wins) {
07127         // Kill event thread
07128         //pthread_cancel(*cimg::X11_attr().event_thread);
07129         //XUnlockDisplay(cimg::X11_attr().display);
07130         //pthread_join(*cimg::X11_attr().event_thread,0);
07131         //delete cimg::X11_attr().event_thread;
07132         //cimg::X11_attr().event_thread = 0;
07133         // XUnlockDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required).
07134         // XCloseDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required).
07135         //cimg::X11_attr().display = 0;
07136         //delete cimg::X11_attr().gc;
07137         //cimg::X11_attr().gc = 0;
07138       }
07139       return *this;
07140     }
07141 
07142     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
07143                         const unsigned int normalization_type=3,
07144                         const bool fullscreen_flag=false, const bool closed_flag=false) {
07145       if (!dimw || !dimh) return assign();
07146       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
07147       _min = _max = 0;
07148       std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
07149                           (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*_width*_height);
07150       return paint();
07151     }
07152 
07153     template<typename T>
07154     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
07155                         const unsigned int normalization_type=3,
07156                         const bool fullscreen_flag=false, const bool closed_flag=false) {
07157       if (!img) return assign();
07158       CImg<T> tmp;
07159       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
07160       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
07161       if (_normalization==2) _min = (float)nimg.min_max(_max);
07162       return render(nimg).paint();
07163     }
07164 
07165     template<typename T>
07166     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
07167                         const unsigned int normalization_type=3,
07168                         const bool fullscreen_flag=false, const bool closed_flag=false) {
07169       if (!list) return assign();
07170       CImg<T> tmp;
07171       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
07172       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
07173       if (_normalization==2) _min = (float)nimg.min_max(_max);
07174       return render(nimg).paint();
07175     }
07176 
07177     CImgDisplay& assign(const CImgDisplay& disp) {
07178       if (!disp) return assign();
07179       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
07180       std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
07181                                   cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
07182                                   sizeof(unsigned int))*_width*_height);
07183       return paint();
07184     }
07185 
07186     CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) {
07187       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
07188       if (is_empty()) return assign(nwidth,nheight);
07189       const unsigned int
07190         tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
07191         tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
07192         dimx = tmpdimx?tmpdimx:1,
07193         dimy = tmpdimy?tmpdimy:1;
07194       XLockDisplay(cimg::X11_attr().display);
07195       if (_window_width!=dimx || _window_height!=dimy) XResizeWindow(cimg::X11_attr().display,_window,dimx,dimy);
07196       if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
07197       case 8 :  { unsigned char foo = 0; _resize(foo,dimx,dimy,redraw); } break;
07198       case 16 : { unsigned short foo = 0; _resize(foo,dimx,dimy,redraw); } break;
07199       default : { unsigned int foo = 0; _resize(foo,dimx,dimy,redraw); }
07200       }
07201       _window_width = _width = dimx; _window_height = _height = dimy;
07202       _is_resized = false;
07203       XUnlockDisplay(cimg::X11_attr().display);
07204       if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
07205       if (redraw) return paint();
07206       return *this;
07207     }
07208 
07209     CImgDisplay& toggle_fullscreen(const bool redraw=true) {
07210       if (is_empty()) return *this;
07211       if (redraw) {
07212         const unsigned int bufsize = _width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
07213         void *odata = std::malloc(bufsize);
07214         std::memcpy(odata,_data,bufsize);
07215         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
07216         std::memcpy(_data,odata,bufsize);
07217         std::free(odata);
07218         return paint(false);
07219       }
07220       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
07221     }
07222 
07223     CImgDisplay& show() {
07224       if (!is_empty() && _is_closed) {
07225         XLockDisplay(cimg::X11_attr().display);
07226         if (_is_fullscreen) _init_fullscreen();
07227         _map_window();
07228         _is_closed = false;
07229         XUnlockDisplay(cimg::X11_attr().display);
07230         return paint();
07231       }
07232       return *this;
07233     }
07234 
07235     CImgDisplay& close() {
07236       if (!is_empty() && !_is_closed) {
07237         XLockDisplay(cimg::X11_attr().display);
07238         if (_is_fullscreen) _desinit_fullscreen();
07239         XUnmapWindow(cimg::X11_attr().display,_window);
07240         _window_x = _window_y = -1;
07241         _is_closed = true;
07242         XUnlockDisplay(cimg::X11_attr().display);
07243       }
07244       return *this;
07245     }
07246 
07247     CImgDisplay& move(const int posx, const int posy) {
07248       if (is_empty()) return *this;
07249       show();
07250       XLockDisplay(cimg::X11_attr().display);
07251       XMoveWindow(cimg::X11_attr().display,_window,posx,posy);
07252       _window_x = posx; _window_y = posy;
07253       _is_moved = false;
07254       XUnlockDisplay(cimg::X11_attr().display);
07255       return paint();
07256     }
07257 
07258     CImgDisplay& show_mouse() {
07259       if (is_empty()) return *this;
07260       XLockDisplay(cimg::X11_attr().display);
07261       XUndefineCursor(cimg::X11_attr().display,_window);
07262       XUnlockDisplay(cimg::X11_attr().display);
07263       return *this;
07264     }
07265 
07266     CImgDisplay& hide_mouse() {
07267       if (is_empty()) return *this;
07268       XLockDisplay(cimg::X11_attr().display);
07269       const char pix_data[8] = { 0 };
07270       XColor col;
07271       col.red = col.green = col.blue = 0;
07272       Pixmap pix = XCreateBitmapFromData(cimg::X11_attr().display,_window,pix_data,8,8);
07273       Cursor cur = XCreatePixmapCursor(cimg::X11_attr().display,pix,pix,&col,&col,0,0);
07274       XFreePixmap(cimg::X11_attr().display,pix);
07275       XDefineCursor(cimg::X11_attr().display,_window,cur);
07276       XUnlockDisplay(cimg::X11_attr().display);
07277       return *this;
07278     }
07279 
07280     CImgDisplay& set_mouse(const int posx, const int posy) {
07281       if (is_empty() || _is_closed) return *this;
07282       XLockDisplay(cimg::X11_attr().display);
07283       XWarpPointer(cimg::X11_attr().display,0L,_window,0,0,0,0,posx,posy);
07284       _mouse_x = posx; _mouse_y = posy;
07285       _is_moved = false;
07286       XSync(cimg::X11_attr().display, False);
07287       XUnlockDisplay(cimg::X11_attr().display);
07288       return *this;
07289     }
07290 
07291     CImgDisplay& set_title(const char *const format, ...) {
07292       if (is_empty()) return *this;
07293       char tmp[1024] = { 0 };
07294       va_list ap;
07295       va_start(ap, format);
07296       std::vsprintf(tmp,format,ap);
07297       va_end(ap);
07298       if (std::strcmp(_title,tmp)) {
07299         if (_title) delete[] _title;
07300         const unsigned int s = std::strlen(tmp) + 1;
07301         _title = new char[s];
07302         std::memcpy(_title,tmp,s*sizeof(char));
07303         XLockDisplay(cimg::X11_attr().display);
07304         XStoreName(cimg::X11_attr().display,_window,tmp);
07305         XUnlockDisplay(cimg::X11_attr().display);
07306       }
07307       return *this;
07308     }
07309 
07310     template<typename T>
07311     CImgDisplay& display(const CImg<T>& img) {
07312       if (!img)
07313         throw CImgArgumentException(_cimgdisplay_instance
07314                                     "display() : Empty specified image.",
07315                                     cimgdisplay_instance);
07316 
07317       if (is_empty()) assign(img._width,img._height);
07318       return render(img).paint(false);
07319     }
07320 
07321     CImgDisplay& paint(const bool wait_expose=true) {
07322       if (is_empty()) return *this;
07323       XLockDisplay(cimg::X11_attr().display);
07324       _paint(wait_expose);
07325       XUnlockDisplay(cimg::X11_attr().display);
07326       return *this;
07327     }
07328 
07329     template<typename T>
07330     CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
07331       if (!img)
07332         throw CImgArgumentException(_cimgdisplay_instance
07333                                     "render() : Empty specified image.",
07334                                     cimgdisplay_instance);
07335 
07336       if (is_empty()) return *this;
07337       if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2));
07338       if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height)) return render(img.get_resize(_width,_height,1,-100,1));
07339       if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
07340         static const CImg<typename CImg<T>::ucharT> default_palette = CImg<typename CImg<T>::ucharT>::default_LUT256();
07341         return render(img.get_index(default_palette,true,false));
07342       }
07343 
07344       const T
07345         *data1 = img._data,
07346         *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
07347         *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
07348 
07349       if (cimg::X11_attr().blue_first) cimg::swap(data1,data3);
07350       XLockDisplay(cimg::X11_attr().display);
07351 
07352       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
07353         _min = _max = 0;
07354         switch (cimg::X11_attr().nb_bits) {
07355         case 8 : { // 256 color palette, no normalization
07356           _set_colormap(_colormap,img._spectrum);
07357           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[img._width*img._height];
07358           unsigned char *ptrd = (unsigned char*)ndata;
07359           switch (img._spectrum) {
07360           case 1 : for (unsigned int xy = img._width*img._height; xy>0; --xy) (*ptrd++) = (unsigned char)*(data1++);
07361             break;
07362           case 2 : for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07363               const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++);
07364               (*ptrd++) = (R&0xf0) | (G>>4);
07365             } break;
07366           default : for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07367               const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++), B = (unsigned char)*(data3++);
07368               (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
07369             }
07370           }
07371           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
07372         } break;
07373         case 16 : { // 16 bits colors, no normalization
07374           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[img._width*img._height];
07375           unsigned char *ptrd = (unsigned char*)ndata;
07376           const unsigned int M = 248;
07377           switch (img._spectrum) {
07378           case 1 :
07379             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07380               const unsigned char val = (unsigned char)*(data1++), G = val>>2;
07381               *(ptrd++) = (val&M) | (G>>3);
07382               *(ptrd++) = (G<<5) | (G>>1);
07383             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07384               const unsigned char val = (unsigned char)*(data1++), G = val>>2;
07385               *(ptrd++) = (G<<5) | (G>>1);
07386               *(ptrd++) = (val&M) | (G>>3);
07387             }
07388             break;
07389           case 2 :
07390             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07391               const unsigned char G = (unsigned char)*(data2++)>>2;
07392               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
07393               *(ptrd++) = (G<<5);
07394             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07395               const unsigned char G = (unsigned char)*(data2++)>>2;
07396               *(ptrd++) = (G<<5);
07397               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
07398             }
07399             break;
07400           default :
07401             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07402               const unsigned char G = (unsigned char)*(data2++)>>2;
07403               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
07404               *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
07405             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07406               const unsigned char G = (unsigned char)*(data2++)>>2;
07407               *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
07408               *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
07409             }
07410           }
07411           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
07412         } break;
07413         default : { // 24 bits colors, no normalization
07414           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[img._width*img._height];
07415           if (sizeof(int)==4) { // 32 bits int uses optimized version
07416             unsigned int *ptrd = ndata;
07417             switch (img._spectrum) {
07418             case 1 :
07419               if (cimg::X11_attr().byte_order==cimg::endianness())
07420                 for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07421                   const unsigned char val = (unsigned char)*(data1++);
07422                   *(ptrd++) = (val<<16) | (val<<8) | val;
07423                 }
07424               else
07425                for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07426                   const unsigned char val = (unsigned char)*(data1++)<<8;
07427                   *(ptrd++) = (val<<16) | (val<<8) | val;
07428                 }
07429               break;
07430             case 2 :
07431               if (cimg::X11_attr().byte_order==cimg::endianness())
07432                for (unsigned int xy = img._width*img._height; xy>0; --xy)
07433                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
07434               else
07435                for (unsigned int xy = img._width*img._height; xy>0; --xy)
07436                   *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
07437               break;
07438             default :
07439               if (cimg::X11_attr().byte_order==cimg::endianness())
07440                for (unsigned int xy = img._width*img._height; xy>0; --xy)
07441                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
07442               else
07443                for (unsigned int xy = img._width*img._height; xy>0; --xy)
07444                   *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
07445             }
07446           } else {
07447             unsigned char *ptrd = (unsigned char*)ndata;
07448             switch (img._spectrum) {
07449             case 1 :
07450               if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07451                 *(ptrd++) = 0;
07452                 *(ptrd++) = (unsigned char)*(data1++);
07453                 *(ptrd++) = 0;
07454                 *(ptrd++) = 0;
07455               } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07456                 *(ptrd++) = 0;
07457                 *(ptrd++) = 0;
07458                 *(ptrd++) = (unsigned char)*(data1++);
07459                 *(ptrd++) = 0;
07460               }
07461               break;
07462             case 2 :
07463               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
07464               for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07465                 *(ptrd++) = 0;
07466                 *(ptrd++) = (unsigned char)*(data2++);
07467                 *(ptrd++) = (unsigned char)*(data1++);
07468                 *(ptrd++) = 0;
07469               }
07470               break;
07471             default :
07472               if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07473                 *(ptrd++) = 0;
07474                 *(ptrd++) = (unsigned char)*(data1++);
07475                 *(ptrd++) = (unsigned char)*(data2++);
07476                 *(ptrd++) = (unsigned char)*(data3++);
07477               } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07478                 *(ptrd++) = (unsigned char)*(data3++);
07479                 *(ptrd++) = (unsigned char)*(data2++);
07480                 *(ptrd++) = (unsigned char)*(data1++);
07481                 *(ptrd++) = 0;
07482               }
07483             }
07484           }
07485           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; }
07486         }
07487         }
07488       } else {
07489         if (_normalization==3) {
07490           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
07491           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
07492         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
07493         const float delta = _max-_min, mm = delta?delta:1.0f;
07494         switch (cimg::X11_attr().nb_bits) {
07495         case 8 : { // 256 color palette, with normalization
07496           _set_colormap(_colormap,img._spectrum);
07497           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[img._width*img._height];
07498           unsigned char *ptrd = (unsigned char*)ndata;
07499           switch (img._spectrum) {
07500           case 1 : for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07501             const unsigned char R = (unsigned char)(255*(*(data1++)-_min)/mm);
07502             *(ptrd++) = R;
07503           } break;
07504           case 2 : for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07505             const unsigned char
07506               R = (unsigned char)(255*(*(data1++)-_min)/mm),
07507               G = (unsigned char)(255*(*(data2++)-_min)/mm);
07508             (*ptrd++) = (R&0xf0) | (G>>4);
07509           } break;
07510           default :
07511             for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07512               const unsigned char
07513                 R = (unsigned char)(255*(*(data1++)-_min)/mm),
07514                 G = (unsigned char)(255*(*(data2++)-_min)/mm),
07515                 B = (unsigned char)(255*(*(data3++)-_min)/mm);
07516               *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
07517             }
07518           }
07519           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
07520         } break;
07521         case 16 : { // 16 bits colors, with normalization
07522           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[img._width*img._height];
07523           unsigned char *ptrd = (unsigned char*)ndata;
07524           const unsigned int M = 248;
07525           switch (img._spectrum) {
07526           case 1 :
07527             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07528               const unsigned char val = (unsigned char)(255*(*(data1++)-_min)/mm), G = val>>2;
07529               *(ptrd++) = (val&M) | (G>>3);
07530               *(ptrd++) = (G<<5) | (val>>3);
07531             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07532               const unsigned char val = (unsigned char)(255*(*(data1++)-_min)/mm), G = val>>2;
07533               *(ptrd++) = (G<<5) | (val>>3);
07534               *(ptrd++) = (val&M) | (G>>3);
07535             }
07536             break;
07537           case 2 :
07538             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07539               const unsigned char G = (unsigned char)(255*(*(data2++)-_min)/mm)>>2;
07540               *(ptrd++) = ((unsigned char)(255*(*(data1++)-_min)/mm)&M) | (G>>3);
07541               *(ptrd++) = (G<<5);
07542             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07543               const unsigned char G = (unsigned char)(255*(*(data2++)-_min)/mm)>>2;
07544               *(ptrd++) = (G<<5);
07545               *(ptrd++) = ((unsigned char)(255*(*(data1++)-_min)/mm)&M) | (G>>3);
07546             }
07547             break;
07548           default :
07549             if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07550               const unsigned char G = (unsigned char)(255*(*(data2++)-_min)/mm)>>2;
07551               *(ptrd++) = ((unsigned char)(255*(*(data1++)-_min)/mm)&M) | (G>>3);
07552               *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-_min)/mm)>>3);
07553             } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07554               const unsigned char G = (unsigned char)(255*(*(data2++)-_min)/mm)>>2;
07555               *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-_min)/mm)>>3);
07556               *(ptrd++) = ((unsigned char)(255*(*(data1++)-_min)/mm)&M) | (G>>3);
07557             }
07558           }
07559           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
07560         } break;
07561         default : { // 24 bits colors, with normalization
07562           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[img._width*img._height];
07563           if (sizeof(int)==4) { // 32 bits int uses optimized version
07564             unsigned int *ptrd = ndata;
07565             switch (img._spectrum) {
07566             case 1 :
07567               if (cimg::X11_attr().byte_order==cimg::endianness())
07568                 for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07569                   const unsigned char val = (unsigned char)(255*(*(data1++)-_min)/mm);
07570                   *(ptrd++) = (val<<16) | (val<<8) | val;
07571                 }
07572               else
07573                 for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07574                   const unsigned char val = (unsigned char)(255*(*(data1++)-_min)/mm);
07575                   *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
07576                 }
07577               break;
07578             case 2 :
07579               if (cimg::X11_attr().byte_order==cimg::endianness())
07580                 for (unsigned int xy = img._width*img._height; xy>0; --xy)
07581                   *(ptrd++) =
07582                     ((unsigned char)(255*(*(data1++)-_min)/mm)<<16) |
07583                     ((unsigned char)(255*(*(data2++)-_min)/mm)<<8);
07584               else
07585                 for (unsigned int xy = img._width*img._height; xy>0; --xy)
07586                   *(ptrd++) =
07587                     ((unsigned char)(255*(*(data2++)-_min)/mm)<<16) |
07588                     ((unsigned char)(255*(*(data1++)-_min)/mm)<<8);
07589               break;
07590             default :
07591               if (cimg::X11_attr().byte_order==cimg::endianness())
07592                 for (unsigned int xy = img._width*img._height; xy>0; --xy)
07593                   *(ptrd++) =
07594                     ((unsigned char)(255*(*(data1++)-_min)/mm)<<16) |
07595                     ((unsigned char)(255*(*(data2++)-_min)/mm)<<8) |
07596                     (unsigned char)(255*(*(data3++)-_min)/mm);
07597               else
07598                 for (unsigned int xy = img._width*img._height; xy>0; --xy)
07599                   *(ptrd++) =
07600                     ((unsigned char)(255*(*(data3++)-_min)/mm)<<24) |
07601                     ((unsigned char)(255*(*(data2++)-_min)/mm)<<16) |
07602                     ((unsigned char)(255*(*(data1++)-_min)/mm)<<8);
07603             }
07604           } else {
07605             unsigned char *ptrd = (unsigned char*)ndata;
07606             switch (img._spectrum) {
07607             case 1 :
07608               if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07609                 const unsigned char val = (unsigned char)(255*(*(data1++)-_min)/mm);
07610                 (*ptrd++) = 0;
07611                 (*ptrd++) = val;
07612                 (*ptrd++) = val;
07613                 (*ptrd++) = val;
07614               } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07615                 const unsigned char val = (unsigned char)(255*(*(data1++)-_min)/mm);
07616                 (*ptrd++) = val;
07617                 (*ptrd++) = val;
07618                 (*ptrd++) = val;
07619                 (*ptrd++) = 0;
07620               }
07621               break;
07622             case 2 :
07623               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
07624               for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07625                 (*ptrd++) = 0;
07626                 (*ptrd++) = (unsigned char)(255*(*(data2++)-_min)/mm);
07627                 (*ptrd++) = (unsigned char)(255*(*(data1++)-_min)/mm);
07628                 (*ptrd++) = 0;
07629               }
07630               break;
07631             default :
07632               if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07633                 (*ptrd++) = 0;
07634                 (*ptrd++) = (unsigned char)(255*(*(data1++)-_min)/mm);
07635                 (*ptrd++) = (unsigned char)(255*(*(data2++)-_min)/mm);
07636                 (*ptrd++) = (unsigned char)(255*(*(data3++)-_min)/mm);
07637               } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07638                 (*ptrd++) = (unsigned char)(255*(*(data3++)-_min)/mm);
07639                 (*ptrd++) = (unsigned char)(255*(*(data2++)-_min)/mm);
07640                 (*ptrd++) = (unsigned char)(255*(*(data1++)-_min)/mm);
07641                 (*ptrd++) = 0;
07642               }
07643             }
07644           }
07645           if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; }
07646         }
07647         }
07648       }
07649       XUnlockDisplay(cimg::X11_attr().display);
07650       return *this;
07651     }
07652 
07653     template<typename T>
07654     const CImgDisplay& snapshot(CImg<T>& img) const {
07655       if (is_empty()) { img.assign(); return *this; }
07656       const unsigned char *ptrs = (unsigned char*)_data;
07657       img.assign(_width,_height,1,3);
07658       T
07659         *data1 = img.data(0,0,0,0),
07660         *data2 = img.data(0,0,0,1),
07661         *data3 = img.data(0,0,0,2);
07662       if (cimg::X11_attr().blue_first) cimg::swap(data1,data3);
07663       switch (cimg::X11_attr().nb_bits) {
07664       case 8 : {
07665         for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07666           const unsigned char val = *(ptrs++);
07667           *(data1++) = val&0xe0;
07668           *(data2++) = (val&0x1c)<<3;
07669           *(data3++) = val<<6;
07670         }
07671       } break;
07672       case 16 : {
07673         if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07674           const unsigned char val0 = *(ptrs++), val1 = *(ptrs++);
07675           *(data1++) = val0&0xf8;
07676           *(data2++) = (val0<<5) | ((val1&0xe0)>>5);
07677           *(data3++) = val1<<3;
07678         } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07679           const unsigned short val0 = *(ptrs++), val1 = *(ptrs++);
07680           *(data1++) = val1&0xf8;
07681           *(data2++) = (val1<<5) | ((val0&0xe0)>>5);
07682           *(data3++) = val0<<3;
07683         }
07684       } break;
07685       default : {
07686         if (cimg::X11_attr().byte_order) for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07687           ++ptrs;
07688           *(data1++) = *(ptrs++);
07689           *(data2++) = *(ptrs++);
07690           *(data3++) = *(ptrs++);
07691         } else for (unsigned int xy = img._width*img._height; xy>0; --xy) {
07692           *(data3++) = *(ptrs++);
07693           *(data2++) = *(ptrs++);
07694           *(data1++) = *(ptrs++);
07695           ++ptrs;
07696         }
07697       }
07698       }
07699       return *this;
07700     }
07701 
07702     // Windows-based implementation.
07703     //-------------------------------
07704 #elif cimg_display==2
07705 
07706     CLIENTCREATESTRUCT _ccs;
07707     BITMAPINFO _bmi;
07708     unsigned int *_data;
07709     DEVMODE _curr_mode;
07710     HWND _window;
07711     HWND _background_window;
07712     HDC _hdc;
07713     HANDLE _thread;
07714     HANDLE _is_created;
07715     HANDLE _mutex;
07716     bool _is_mouse_tracked;
07717     bool _is_cursor_visible;
07718 
07719     static int screen_width() {
07720       DEVMODE mode;
07721       mode.dmSize = sizeof(DEVMODE);
07722       mode.dmDriverExtra = 0;
07723       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
07724       return mode.dmPelsWidth;
07725     }
07726 
07727     static int screen_height() {
07728       DEVMODE mode;
07729       mode.dmSize = sizeof(DEVMODE);
07730       mode.dmDriverExtra = 0;
07731       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
07732       return mode.dmPelsHeight;
07733     }
07734 
07735     static void wait_all() {
07736       WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
07737     }
07738 
07739     static LRESULT APIENTRY _handle_events(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) {
07740 #ifdef _WIN64
07741       CImgDisplay *disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
07742 #else
07743       CImgDisplay *disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
07744 #endif
07745       MSG st_msg;
07746       switch (msg) {
07747       case WM_CLOSE :
07748         disp->_mouse_x = disp->_mouse_y = -1;
07749         disp->_window_x = disp->_window_y = 0;
07750         disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
07751         ReleaseMutex(disp->_mutex);
07752         ShowWindow(disp->_window,SW_HIDE);
07753         disp->_is_event = true;
07754         SetEvent(cimg::Win32_attr().wait_event);
07755         return 0;
07756       case WM_SIZE : {
07757         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
07758         WaitForSingleObject(disp->_mutex,INFINITE);
07759         const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
07760         if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
07761           disp->_window_width = nw;
07762           disp->_window_height = nh;
07763           disp->_mouse_x = disp->_mouse_y = -1;
07764           disp->_is_resized = disp->_is_event = true;
07765           SetEvent(cimg::Win32_attr().wait_event);
07766         }
07767         ReleaseMutex(disp->_mutex);
07768       } break;
07769       case WM_MOVE : {
07770         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
07771         WaitForSingleObject(disp->_mutex,INFINITE);
07772         const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
07773         if (nx!=disp->_window_x || ny!=disp->_window_y) {
07774           disp->_window_x = nx;
07775           disp->_window_y = ny;
07776           disp->_is_moved = disp->_is_event = true;
07777           SetEvent(cimg::Win32_attr().wait_event);
07778         }
07779         ReleaseMutex(disp->_mutex);
07780       } break;
07781       case WM_PAINT :
07782         disp->paint();
07783         break;
07784       case WM_KEYDOWN :
07785         disp->set_key((unsigned int)wParam);
07786         SetEvent(cimg::Win32_attr().wait_event);
07787         break;
07788       case WM_KEYUP :
07789         disp->set_key((unsigned int)wParam,false);
07790         SetEvent(cimg::Win32_attr().wait_event);
07791         break;
07792       case WM_MOUSEMOVE : {
07793         while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
07794         disp->_mouse_x = LOWORD(lParam);
07795         disp->_mouse_y = HIWORD(lParam);
07796 #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
07797         if (!disp->_is_mouse_tracked) {
07798           TRACKMOUSEEVENT tme;
07799           tme.cbSize = sizeof(TRACKMOUSEEVENT);
07800           tme.dwFlags = TME_LEAVE;
07801           tme.hwndTrack = disp->_window;
07802           if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
07803         }
07804 #endif
07805         if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
07806           disp->_mouse_x = disp->_mouse_y = -1;
07807         disp->_is_event = true;
07808         SetEvent(cimg::Win32_attr().wait_event);
07809       } break;
07810       case WM_MOUSELEAVE : {
07811         disp->_mouse_x = disp->_mouse_y = -1;
07812         disp->_is_mouse_tracked = false;
07813       } break;
07814       case WM_LBUTTONDOWN :
07815         disp->set_button(1);
07816         SetEvent(cimg::Win32_attr().wait_event);
07817         break;
07818       case WM_RBUTTONDOWN :
07819         disp->set_button(2);
07820         SetEvent(cimg::Win32_attr().wait_event);
07821         break;
07822       case WM_MBUTTONDOWN :
07823         disp->set_button(3);
07824         SetEvent(cimg::Win32_attr().wait_event);
07825         break;
07826       case WM_LBUTTONUP :
07827         disp->set_button(1,false);
07828         SetEvent(cimg::Win32_attr().wait_event);
07829         break;
07830       case WM_RBUTTONUP :
07831         disp->set_button(2,false);
07832         SetEvent(cimg::Win32_attr().wait_event);
07833         break;
07834       case WM_MBUTTONUP :
07835         disp->set_button(3,false);
07836         SetEvent(cimg::Win32_attr().wait_event);
07837         break;
07838       case 0x020A : // WM_MOUSEWHEEL:
07839         disp->set_wheel((int)((short)HIWORD(wParam))/120);
07840         SetEvent(cimg::Win32_attr().wait_event);
07841       case WM_SETCURSOR :
07842         if (disp->_is_cursor_visible) ShowCursor(TRUE);
07843         else ShowCursor(FALSE);
07844         break;
07845       }
07846       return DefWindowProc(window,msg,wParam,lParam);
07847     }
07848 
07849     static DWORD WINAPI _events_thread(void* arg) {
07850       CImgDisplay *disp = (CImgDisplay*)(((void**)arg)[0]);
07851       const char *const title = (const char*)(((void**)arg)[1]);
07852       MSG msg;
07853       delete[] (void**)arg;
07854       disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
07855       disp->_bmi.bmiHeader.biWidth = disp->width();
07856       disp->_bmi.bmiHeader.biHeight = -disp->height();
07857       disp->_bmi.bmiHeader.biPlanes = 1;
07858       disp->_bmi.bmiHeader.biBitCount = 32;
07859       disp->_bmi.bmiHeader.biCompression = BI_RGB;
07860       disp->_bmi.bmiHeader.biSizeImage = 0;
07861       disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
07862       disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
07863       disp->_bmi.bmiHeader.biClrUsed = 0;
07864       disp->_bmi.bmiHeader.biClrImportant = 0;
07865       disp->_data = new unsigned int[disp->_width*disp->_height];
07866       if (!disp->_is_fullscreen) { // Normal window
07867         RECT rect;
07868         rect.left = rect.top = 0; rect.right = disp->_width-1; rect.bottom = disp->_height-1;
07869         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
07870         const int
07871           border1 = (rect.right - rect.left + 1 - disp->_width)/2,
07872           border2 = rect.bottom - rect.top + 1 - disp->_height - border1;
07873         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
07874                                      WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT,
07875                                      disp->_width + 2*border1, disp->_height + border1 + border2,
07876                                      0,0,0,&(disp->_ccs));
07877         if (!disp->_is_closed) {
07878           GetWindowRect(disp->_window,&rect);
07879           disp->_window_x = rect.left + border1;
07880           disp->_window_y = rect.top + border2;
07881         } else disp->_window_x = disp->_window_y = 0;
07882       } else { // Fullscreen window
07883         const unsigned int sx = screen_width(), sy = screen_height();
07884         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
07885                                      WS_POPUP | (disp->_is_closed?0:WS_VISIBLE), (sx-disp->_width)/2, (sy-disp->_height)/2,
07886                                      disp->_width,disp->_height,0,0,0,&(disp->_ccs));
07887         disp->_window_x = disp->_window_y = 0;
07888       }
07889       SetForegroundWindow(disp->_window);
07890       disp->_hdc = GetDC(disp->_window);
07891       disp->_window_width = disp->_width;
07892       disp->_window_height = disp->_height;
07893       disp->flush();
07894 #ifdef _WIN64
07895       SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
07896       SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
07897 #else
07898       SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
07899       SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
07900 #endif
07901       SetEvent(disp->_is_created);
07902       while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
07903       return 0;
07904     }
07905 
07906     CImgDisplay& _update_window_pos() {
07907       if (!_is_closed) {
07908         RECT rect;
07909         rect.left = rect.top = 0; rect.right = _width-1; rect.bottom = _height-1;
07910         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
07911         const int
07912           border1 = (rect.right - rect.left + 1 - _width)/2,
07913           border2 = rect.bottom - rect.top + 1 - _height - border1;
07914         GetWindowRect(_window,&rect);
07915         _window_x = rect.left + border1;
07916         _window_y = rect.top + border2;
07917       } else _window_x = _window_y = -1;
07918       return *this;
07919     }
07920 
07921     void _init_fullscreen() {
07922       _background_window = 0;
07923       if (_is_fullscreen && !_is_closed) {
07924         DEVMODE mode;
07925         unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
07926         for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
07927           const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
07928           if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
07929             bestbpp = mode.dmBitsPerPel;
07930             ibest = imode;
07931             bw = nw; bh = nh;
07932           }
07933         }
07934         if (bestbpp) {
07935           _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
07936           EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
07937           EnumDisplaySettings(0,ibest,&mode);
07938           ChangeDisplaySettings(&mode,0);
07939         } else _curr_mode.dmSize = 0;
07940 
07941         const unsigned int sx = screen_width(), sy = screen_height();
07942         if (sx!=_width || sy!=_height) {
07943           CLIENTCREATESTRUCT background_ccs;
07944           _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs);
07945           SetForegroundWindow(_background_window);
07946         }
07947       } else _curr_mode.dmSize = 0;
07948     }
07949 
07950     void _desinit_fullscreen() {
07951       if (_is_fullscreen) {
07952         if (_background_window) DestroyWindow(_background_window);
07953         _background_window = 0;
07954         if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
07955         _is_fullscreen = false;
07956       }
07957     }
07958 
07959     CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
07960                          const unsigned int normalization_type=3,
07961                          const bool fullscreen_flag=false, const bool closed_flag=false) {
07962 
07963       // Allocate space for window title
07964       const char *const nptitle = ptitle?ptitle:"";
07965       const unsigned int s = std::strlen(nptitle) + 1;
07966       char *const tmp_title = s?new char[s]:0;
07967       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
07968 
07969       // Destroy previous window if existing
07970       if (!is_empty()) assign();
07971 
07972       // Set display variables
07973       _width = cimg::min(dimw,(unsigned int)screen_width());
07974       _height = cimg::min(dimh,(unsigned int)screen_height());
07975       _normalization = normalization_type<4?normalization_type:3;
07976       _is_fullscreen = fullscreen_flag;
07977       _window_x = _window_y = 0;
07978       _is_closed = closed_flag;
07979       _is_cursor_visible = true;
07980       _is_mouse_tracked = false;
07981       _title = tmp_title;
07982       flush();
07983       if (_is_fullscreen) _init_fullscreen();
07984 
07985       // Create event thread
07986       void *const arg = (void*)(new void*[2]);
07987       ((void**)arg)[0] = (void*)this;
07988       ((void**)arg)[1] = (void*)_title;
07989       unsigned long ThreadID = 0;
07990       _mutex = CreateMutex(0,FALSE,0);
07991       _is_created = CreateEvent(0,FALSE,FALSE,0);
07992       _thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID);
07993       WaitForSingleObject(_is_created,INFINITE);
07994       return *this;
07995     }
07996 
07997     CImgDisplay& assign() {
07998       if (is_empty()) return *this;
07999       DestroyWindow(_window);
08000       TerminateThread(_thread,0);
08001       if (_data) delete[] _data;
08002       if (_title) delete[] _title;
08003       if (_is_fullscreen) _desinit_fullscreen();
08004       _width = _height = _normalization = _window_width = _window_height = 0;
08005       _window_x = _window_y = 0;
08006       _is_fullscreen = false;
08007       _is_closed = true;
08008       _min = _max = 0;
08009       _title = 0;
08010       flush();
08011       return *this;
08012     }
08013 
08014     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
08015                         const unsigned int normalization_type=3,
08016                         const bool fullscreen_flag=false, const bool closed_flag=false) {
08017       if (!dimw || !dimh) return assign();
08018       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
08019       _min = _max = 0;
08020       std::memset(_data,0,sizeof(unsigned int)*_width*_height);
08021       return paint();
08022     }
08023 
08024     template<typename T>
08025     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
08026                         const unsigned int normalization_type=3,
08027                         const bool fullscreen_flag=false, const bool closed_flag=false) {
08028       if (!img) return assign();
08029       CImg<T> tmp;
08030       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
08031       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
08032       if (_normalization==2) _min = (float)nimg.min_max(_max);
08033       return display(nimg);
08034     }
08035 
08036     template<typename T>
08037     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
08038                         const unsigned int normalization_type=3,
08039                         const bool fullscreen_flag=false, const bool closed_flag=false) {
08040       if (!list) return assign();
08041       CImg<T> tmp;
08042       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
08043       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
08044       if (_normalization==2) _min = (float)nimg.min_max(_max);
08045       return display(nimg);
08046     }
08047 
08048     CImgDisplay& assign(const CImgDisplay& disp) {
08049       if (!disp) return assign();
08050       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
08051       std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
08052       return paint();
08053     }
08054 
08055     CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) {
08056       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
08057       if (is_empty()) return assign(nwidth,nheight);
08058       const unsigned int
08059         tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
08060         tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
08061         dimx = tmpdimx?tmpdimx:1,
08062         dimy = tmpdimy?tmpdimy:1;
08063       if (_window_width!=dimx || _window_height!=dimy) {
08064         RECT rect; rect.left = rect.top = 0; rect.right = dimx - 1; rect.bottom = dimy - 1;
08065         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
08066         const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
08067         SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
08068       }
08069       if (_width!=dimx || _height!=dimy) {
08070         unsigned int *const ndata = new unsigned int[dimx*dimy];
08071         if (redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
08072         else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
08073         delete[] _data;
08074         _data = ndata;
08075         _bmi.bmiHeader.biWidth = dimx;
08076         _bmi.bmiHeader.biHeight = -(int)dimy;
08077         _width = dimx;
08078         _height = dimy;
08079       }
08080       _window_width = dimx; _window_height = dimy;
08081       _is_resized = false;
08082       if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
08083       if (redraw) return paint();
08084       return *this;
08085     }
08086 
08087     CImgDisplay& toggle_fullscreen(const bool redraw=true) {
08088       if (is_empty()) return *this;
08089       if (redraw) {
08090         const unsigned int bufsize = _width*_height*4;
08091         void *odata = std::malloc(bufsize);
08092         std::memcpy(odata,_data,bufsize);
08093         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
08094         std::memcpy(_data,odata,bufsize);
08095         std::free(odata);
08096         return paint();
08097       }
08098       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
08099     }
08100 
08101     CImgDisplay& show() {
08102       if (is_empty()) return *this;
08103       if (_is_closed) {
08104         _is_closed = false;
08105         if (_is_fullscreen) _init_fullscreen();
08106         ShowWindow(_window,SW_SHOW);
08107         _update_window_pos();
08108       }
08109       return paint();
08110     }
08111 
08112     CImgDisplay& close() {
08113       if (is_empty()) return *this;
08114       if (!_is_closed && !_is_fullscreen) {
08115         if (_is_fullscreen) _desinit_fullscreen();
08116         ShowWindow(_window,SW_HIDE);
08117         _is_closed = true;
08118         _window_x = _window_y = 0;
08119       }
08120       return *this;
08121     }
08122 
08123     CImgDisplay& move(const int posx, const int posy) {
08124       if (is_empty()) return *this;
08125       if (!_is_fullscreen) {
08126         RECT rect; rect.left = rect.top = 0; rect.right = _window_width-1; rect.bottom = _window_height-1;
08127         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
08128         const int border1 = (rect.right-rect.left+1-_width)/2, border2 = rect.bottom-rect.top+1-_height-border1;
08129         SetWindowPos(_window,0,posx-border1,posy-border2,0,0,SWP_NOSIZE | SWP_NOZORDER);
08130       } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
08131       _window_x = posx;
08132       _window_y = posy;
08133       _is_moved = false;
08134       return show();
08135     }
08136 
08137     CImgDisplay& show_mouse() {
08138       if (is_empty()) return *this;
08139       _is_cursor_visible = true;
08140       ShowCursor(TRUE);
08141       SendMessage(_window,WM_SETCURSOR,0,0);
08142       return *this;
08143     }
08144 
08145     CImgDisplay& hide_mouse() {
08146       if (is_empty()) return *this;
08147       _is_cursor_visible = false;
08148       ShowCursor(FALSE);
08149       SendMessage(_window,WM_SETCURSOR,0,0);
08150       return *this;
08151     }
08152 
08153     CImgDisplay& set_mouse(const int posx, const int posy) {
08154       if (!_is_closed && posx>=0 && posy>=0) {
08155         _update_window_pos();
08156         const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
08157         if (res) { _mouse_x = posx; _mouse_y = posy; }
08158       }
08159       return *this;
08160     }
08161 
08162     CImgDisplay& set_title(const char *const format, ...) {
08163       if (is_empty()) return *this;
08164       char tmp[1024] = { 0 };
08165       va_list ap;
08166       va_start(ap, format);
08167       std::vsprintf(tmp,format,ap);
08168       va_end(ap);
08169       if (std::strcmp(_title,tmp)) {
08170         if (_title) delete[] _title;
08171         const unsigned int s = std::strlen(tmp) + 1;
08172         _title = new char[s];
08173         std::memcpy(_title,tmp,s*sizeof(char));
08174         SetWindowTextA(_window, tmp);
08175       }
08176       return *this;
08177     }
08178 
08179     template<typename T>
08180     CImgDisplay& display(const CImg<T>& img) {
08181       if (!img)
08182         throw CImgArgumentException(_cimgdisplay_instance
08183                                     "display() : Empty specified image.",
08184                                     cimgdisplay_instance);
08185 
08186       if (is_empty()) assign(img._width,img._height);
08187       return render(img).paint();
08188     }
08189 
08190     CImgDisplay& paint() {
08191       if (!_is_closed) {
08192         WaitForSingleObject(_mutex,INFINITE);
08193         SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
08194         ReleaseMutex(_mutex);
08195       }
08196       return *this;
08197     }
08198 
08199     template<typename T>
08200     CImgDisplay& render(const CImg<T>& img) {
08201       if (!img)
08202         throw CImgArgumentException(_cimgdisplay_instance
08203                                     "render() : Empty specified image.",
08204                                     cimgdisplay_instance);
08205 
08206       if (is_empty()) return *this;
08207       if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2));
08208 
08209       const T
08210         *data1 = img._data,
08211         *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
08212         *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
08213 
08214       WaitForSingleObject(_mutex,INFINITE);
08215       unsigned int
08216         *const ndata = (img._width==_width && img._height==_height)?_data:new unsigned int[img._width*img._height],
08217         *ptrd = ndata;
08218 
08219       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
08220         _min = _max = 0;
08221         switch (img._spectrum) {
08222         case 1 : {
08223           for (unsigned int xy = img._width*img._height; xy>0; --xy) {
08224             const unsigned char val = (unsigned char)*(data1++);
08225             *(ptrd++) = (val<<16) | (val<<8) | val;
08226           }
08227         } break;
08228         case 2 : {
08229           for (unsigned int xy = img._width*img._height; xy>0; --xy)
08230             *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
08231         } break;
08232         default : {
08233           for (unsigned int xy = img._width*img._height; xy>0; --xy)
08234             *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
08235         }
08236         }
08237       } else {
08238         if (_normalization==3) {
08239           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
08240           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
08241         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
08242         const float delta = _max-_min, mm = delta?delta:1.0f;
08243         switch (img._spectrum) {
08244         case 1 : {
08245           for (unsigned int xy = img._width*img._height; xy>0; --xy) {
08246             const unsigned char val = (unsigned char)(255*(*(data1++)-_min)/mm);
08247             *(ptrd++) = (val<<16) | (val<<8) | val;
08248           }
08249         } break;
08250         case 2 : {
08251           for (unsigned int xy = img._width*img._height; xy>0; --xy) {
08252             const unsigned char
08253               R = (unsigned char)(255*(*(data1++)-_min)/mm),
08254               G = (unsigned char)(255*(*(data2++)-_min)/mm);
08255             *(ptrd++) = (R<<16) | (G<<8);
08256           }
08257         } break;
08258         default : {
08259           for (unsigned int xy = img._width*img._height; xy>0; --xy) {
08260             const unsigned char
08261               R = (unsigned char)(255*(*(data1++)-_min)/mm),
08262               G = (unsigned char)(255*(*(data2++)-_min)/mm),
08263               B = (unsigned char)(255*(*(data3++)-_min)/mm);
08264             *(ptrd++) = (R<<16) | (G<<8) | B;
08265           }
08266         }
08267         }
08268       }
08269       if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
08270       ReleaseMutex(_mutex);
08271       return *this;
08272     }
08273 
08274     template<typename T>
08275     const CImgDisplay& snapshot(CImg<T>& img) const {
08276       if (is_empty()) { img.assign(); return *this; }
08277       const unsigned int *ptrs = _data;
08278       img.assign(_width,_height,1,3);
08279       T
08280         *data1 = img.data(0,0,0,0),
08281         *data2 = img.data(0,0,0,1),
08282         *data3 = img.data(0,0,0,2);
08283       for (unsigned int xy = img._width*img._height; xy>0; --xy) {
08284         const unsigned int val = *(ptrs++);
08285         *(data1++) = (unsigned char)(val>>16);
08286         *(data2++) = (unsigned char)((val>>8)&0xFF);
08287         *(data3++) = (unsigned char)(val&0xFF);
08288       }
08289       return *this;
08290     }
08291 #endif
08292 
08293     //@}
08294   };
08295 
08296   /*
08297    #--------------------------------------
08298    #
08299    #
08300    #
08301    # Definition of the CImg<T> structure
08302    #
08303    #
08304    #
08305    #--------------------------------------
08306    */
08307 
08308   //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
08309   /**
08310      This is the main class of the %CImg Library. It declares and constructs
08311      an image, allows access to its pixel values, and is able to perform various image operations.
08312 
08313      \par Image representation
08314 
08315      A %CImg image is defined as an instance of the container \ref CImg<\c T>, which contains a regular grid of pixels,
08316      each pixel value being of type \c T. The image grid can have up to 4 dimensions : width, height, depth
08317      and number of channels.
08318      Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>, while the number of channels
08319      is rather used as a vector-valued dimension (it may describe the R,G,B color channels for instance).
08320      If you need a fifth dimension, you can use image lists \ref CImgList<\c T> rather than simple images \ref CImg<\c T>.
08321 
08322      Thus, the \ref CImg<\c T> class is able to represent volumetric images of vector-valued pixels,
08323      as well as images with less dimensions (1d scalar signal, 2d color images, ...).
08324      Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
08325 
08326      Concerning the pixel value type \c T :
08327      fully supported template types are the basic C++ types : <tt>unsigned char, char, short, unsigned int, int,
08328      unsigned long, long, float, double, ... </tt>.
08329      Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
08330      while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
08331      images that have floating-point pixel values. The default value for the template T is \c float.
08332      Using your own template types may be possible. However, you will certainly have to define the complete set
08333      of arithmetic and logical operators for your class.
08334 
08335      \par Image structure
08336 
08337      The \ref CImg<\c T> structure contains \a six fields :
08338      - \ref width defines the number of \a columns of the image (size along the X-axis).
08339      - \ref height defines the number of \a rows of the image (size along the Y-axis).
08340      - \ref depth defines the number of \a slices of the image (size along the Z-axis).
08341      - \ref spectrum defines the number of \a channels of the image (size along the C-axis).
08342      - \ref data defines a \a pointer to the \a pixel \a data (of type \c T).
08343      - \ref is_shared is a boolean that tells if the memory buffer \ref data is shared with
08344        another image.
08345 
08346      You can access these fields publicly although it is recommended to use the dedicated functions
08347      width(), height(), depth(), spectrum() and ptr() to do so.
08348      Image dimensions are not limited to a specific range (as long as you got enough available memory).
08349      A value of \e 1 usually means that the corresponding dimension is \a flat.
08350      If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
08351      Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
08352      (a CImgInstanceException will be thrown instead).
08353      Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
08354 
08355      \par Image declaration and construction
08356 
08357      Declaring an image can be done by using one of the several available constructors.
08358      Here is a list of the most used :
08359 
08360      - Construct images from arbitrary dimensions :
08361          - <tt>CImg<char> img;</tt> declares an empty image.
08362          - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
08363          \c unsigned \c char pixel values.
08364          - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
08365          - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
08366          (colors are stored as an image with three channels).
08367          - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
08368          (with \c double pixel values).
08369          - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
08370          (with \c float pixels, which is the default value of the template parameter \c T).
08371          - \b Note : images pixels are <b>not automatically initialized to 0</b>. You may use the function \ref fill() to
08372          do it, or use the specific constructor taking 5 parameters like this :
08373          <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
08374 
08375      - Construct images from filenames :
08376          - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
08377          - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr".
08378          - \b Note : You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
08379          to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
08380 
08381      - Construct images from C-style arrays :
08382          - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
08383          \c data_buffer (of size 256x256=65536).
08384          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3,false);</tt> constructs a 256x256 color image
08385          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
08386          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3,true);</tt> constructs a 256x256 color image
08387          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed).
08388 
08389          The complete list of constructors can be found <a href="#constructors">here</a>.
08390 
08391      \par Most useful functions
08392 
08393      The \ref CImg<\c T> class contains a lot of functions that operates on images.
08394      Some of the most useful are :
08395 
08396      - operator()() : allows to access or write pixel values.
08397      - display() : displays the image in a new window.
08398   **/
08399   template<typename T>
08400   struct CImg {
08401 
08402     //! Variable representing the width of the instance image (i.e. dimensions along the X-axis).
08403     /**
08404        \remark
08405        - Prefer using the function CImg<T>::width() to get information about the width of an image.
08406        - Use function CImg<T>::resize() to set a new width for an image. Setting directly the variable \c width would probably
08407        result in a library crash.
08408        - Empty images have \c width defined to \c 0.
08409     **/
08410     unsigned int _width;
08411 
08412     //! Variable representing the height of the instance image (i.e. dimensions along the Y-axis).
08413     /**
08414        \remark
08415        - Prefer using the function CImg<T>::height() to get information about the height of an image.
08416        - Use function CImg<T>::resize() to set a new height for an image. Setting directly the variable \c height would probably
08417        result in a library crash.
08418        - 1d signals have \c height defined to \c 1.
08419        - Empty images have \c height defined to \c 0.
08420     **/
08421     unsigned int _height;
08422 
08423     //! Variable representing the depth of the instance image (i.e. dimensions along the Z-axis).
08424     /**
08425        \remark
08426        - Prefer using the function CImg<T>::depth() to get information about the depth of an image.
08427        - Use function CImg<T>::resize() to set a new depth for an image. Setting directly the variable \c depth would probably
08428        result in a library crash.
08429        - Classical 2d images have \c depth defined to \c 1.
08430        - Empty images have \c depth defined to \c 0.
08431     **/
08432     unsigned int _depth;
08433 
08434     //! Variable representing the number of channels of the instance image (i.e. dimensions along the C-axis).
08435     /**
08436        \remark
08437        - Prefer using the function CImg<T>::spectrum() to get information about the depth of an image.
08438        - Use function CImg<T>::resize() to set a new vector dimension for an image. Setting directly the variable \c spectrum would probably
08439        result in a library crash.
08440        - Scalar-valued images (one value per pixel) have \c spectrum defined to \c 1.
08441        - Empty images have \c depth defined to \c 0.
08442     **/
08443     unsigned int _spectrum;
08444 
08445     //! Variable telling if pixel buffer of the instance image is shared with another one.
08446     bool _is_shared;
08447 
08448     //! Pointer to the first pixel of the pixel buffer.
08449     T *_data;
08450 
08451     //! Iterator type for CImg<T>.
08452     /**
08453        \remark
08454        - An \p iterator is a <tt>T*</tt> pointer (address of a pixel value in the pixel buffer).
08455        - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL.
08456     **/
08457     typedef T* iterator;
08458 
08459     //! Const iterator type for CImg<T>.
08460     /**
08461        \remark
08462        - A \p const_iterator is a <tt>const T*</tt> pointer (address of a pixel value in the pixel buffer).
08463        - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL.
08464     **/
08465     typedef const T* const_iterator;
08466 
08467     //! Value type.
08468     typedef T value_type;
08469 
08470     // Define common T-dependant types.
08471     typedef typename cimg::superset<T,bool>::type Tbool;
08472     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
08473     typedef typename cimg::superset<T,char>::type Tchar;
08474     typedef typename cimg::superset<T,unsigned short>::type Tushort;
08475     typedef typename cimg::superset<T,short>::type Tshort;
08476     typedef typename cimg::superset<T,unsigned int>::type Tuint;
08477     typedef typename cimg::superset<T,int>::type Tint;
08478     typedef typename cimg::superset<T,unsigned long>::type Tulong;
08479     typedef typename cimg::superset<T,long>::type Tlong;
08480     typedef typename cimg::superset<T,float>::type Tfloat;
08481     typedef typename cimg::superset<T,double>::type Tdouble;
08482     typedef typename cimg::last<T,bool>::type boolT;
08483     typedef typename cimg::last<T,unsigned char>::type ucharT;
08484     typedef typename cimg::last<T,char>::type charT;
08485     typedef typename cimg::last<T,unsigned short>::type ushortT;
08486     typedef typename cimg::last<T,short>::type shortT;
08487     typedef typename cimg::last<T,unsigned int>::type uintT;
08488     typedef typename cimg::last<T,int>::type intT;
08489     typedef typename cimg::last<T,unsigned long>::type ulongT;
08490     typedef typename cimg::last<T,long>::type longT;
08491     typedef typename cimg::last<T,float>::type floatT;
08492     typedef typename cimg::last<T,double>::type doubleT;
08493 
08494     //@}
08495     //---------------------------
08496     //
08497     //! \name Plugins
08498     //@{
08499     //---------------------------
08500 #ifdef cimg_plugin
08501 #include cimg_plugin
08502 #endif
08503 #ifdef cimg_plugin1
08504 #include cimg_plugin1
08505 #endif
08506 #ifdef cimg_plugin2
08507 #include cimg_plugin2
08508 #endif
08509 #ifdef cimg_plugin3
08510 #include cimg_plugin3
08511 #endif
08512 #ifdef cimg_plugin4
08513 #include cimg_plugin4
08514 #endif
08515 #ifdef cimg_plugin5
08516 #include cimg_plugin5
08517 #endif
08518 #ifdef cimg_plugin6
08519 #include cimg_plugin6
08520 #endif
08521 #ifdef cimg_plugin7
08522 #include cimg_plugin7
08523 #endif
08524 #ifdef cimg_plugin8
08525 #include cimg_plugin8
08526 #endif
08527 
08528     //@}
08529     //---------------------------------------------------------
08530     //
08531     //! \name Constructors / Destructor / Instance Management
08532     //@{
08533     //---------------------------------------------------------
08534 
08535     //! Destructor.
08536     /**
08537        The destructor destroys the instance image.
08538        \remark
08539        - Destructing an empty or shared image does nothing.
08540        - Otherwise, all memory used to store the pixel data of the instance image is freed.
08541        - When destroying a non-shared image, be sure that every shared instances of the same image are
08542        also destroyed to avoid further access to desallocated memory buffers.
08543     **/
08544     ~CImg() {
08545       if (_data && !_is_shared) delete[] _data;
08546     }
08547 
08548     //! Default constructor.
08549     /**
08550        The default constructor creates an empty instance image.
08551        \remark
08552        - An empty image does not contain any data and has all of its dimensions \ref width, \ref height, \ref depth, \ref spectrum
08553        set to 0 as well as its pointer to the pixel buffer \ref data.
08554        - An empty image is non-shared.
08555     **/
08556     CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
08557 
08558     //! Constructs a new image with given size (\p dx,\p dy,\p dz,\p dc).
08559     /**
08560        This constructors create an instance image of size (\p dx,\p dy,\p dz,\p dc) with pixels of type \p T.
08561        \param dx Desired size along the X-axis, i.e. the \ref width of the image.
08562        \param dy Desired size along the Y-axis, i.e. the \ref height of the image.
08563        \param dz Desired size along the Z-axis, i.e. the \ref depth of the image.
08564        \param dc Desired size along the C-axis, i.e. the number of image channels \ref spectrum.
08565        \remark
08566        - If one of the input dimension \p dx,\p dy,\p dz or \p dc is set to 0, the created image is empty
08567        and all has its dimensions set to 0. No memory for pixel data is then allocated.
08568        - This constructor creates only non-shared images.
08569        - Image pixels allocated by this constructor are \b not \b initialized.
08570        Use the constructor CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T)
08571        to get an image of desired size with pixels set to a particular value.
08572     **/
08573     explicit CImg(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dc=1):
08574       _is_shared(false) {
08575       const unsigned int siz = dx*dy*dz*dc;
08576       if (siz) {
08577         _width = dx; _height = dy; _depth = dz; _spectrum = dc;
08578         try { _data = new T[siz]; } catch (...) {
08579           _width = _height = _depth = _spectrum = 0; _data = 0;
08580           throw CImgInstanceException(_cimg_instance
08581                                       "CImg() : Failed to allocate memory for image (%u,%u,%u,%u).",
08582                                       cimg_instance,
08583                                       dx,dy,dz,dc);
08584         }
08585       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
08586     }
08587 
08588     //! Construct an image with given size (\p dx,\p dy,\p dz,\p dc) and with pixel having a default value \p val.
08589     /**
08590        This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dc) with pixels of type \p T and sets all pixel
08591        values of the created instance image to \p val.
08592        \param dx Desired size along the X-axis, i.e. the \ref width of the image.
08593        \param dy Desired size along the Y-axis, i.e. the \ref height of the image.
08594        \param dz Desired size along the Z-axis, i.e. the \ref depth of the image.
08595        \param dc Desired size along the C-axis, i.e. the number of image channels \p spectrum.
08596        \param val Default value for image pixels.
08597        \remark
08598        - This constructor has the same properties as CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int).
08599     **/
08600     CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc, const T val):
08601       _is_shared(false) {
08602       const unsigned int siz = dx*dy*dz*dc;
08603       if (siz) {
08604         _width = dx; _height = dy; _depth = dz; _spectrum = dc;
08605         try { _data = new T[siz]; } catch (...) {
08606           _width = _height = _depth = _spectrum = 0; _data = 0;
08607           throw CImgInstanceException(_cimg_instance
08608                                       "CImg() : Failed to allocate memory for image (%u,%u,%u,%u).",
08609                                       cimg_instance,
08610                                       dx,dy,dz,dc);
08611         }
08612         fill(val);
08613       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
08614     }
08615 
08616     //! Construct an image with given size (\p dx,\p dy,\p dz,\p dc) and with specified pixel values (int version).
08617     CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc,
08618          const int val0, const int val1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
08619 #define _CImg_stdarg(img,a0,a1,N,t) { \
08620         unsigned int _siz = (unsigned int)N; \
08621         if (_siz--) { \
08622           va_list ap; \
08623           va_start(ap,a1); \
08624           T *ptrd = (img)._data; \
08625           *(ptrd++) = (T)a0; \
08626           if (_siz--) { \
08627             *(ptrd++) = (T)a1; \
08628             for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
08629           } \
08630           va_end(ap); \
08631         } \
08632       }
08633       assign(dx,dy,dz,dc);
08634       _CImg_stdarg(*this,val0,val1,dx*dy*dz*dc,int);
08635     }
08636 
08637     //! Construct an image with given size (\p dx,\p dy,\p dz,\p dc) and with specified pixel values (double version).
08638     CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc,
08639          const double val0, const double val1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
08640       assign(dx,dy,dz,dc);
08641       _CImg_stdarg(*this,val0,val1,dx*dy*dz*dc,double);
08642     }
08643 
08644     //! Construct an image with given size and with specified values given in a string.
08645     CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc,
08646          const char *const values, const bool repeat_values):_is_shared(false) {
08647       const unsigned int siz = dx*dy*dz*dc;
08648       if (siz) {
08649         _width = dx; _height = dy; _depth = dz; _spectrum = dc;
08650         try { _data = new T[siz]; } catch (...) {
08651           _width = _height = _depth = _spectrum = 0; _data = 0;
08652           throw CImgInstanceException(_cimg_instance
08653                                       "CImg() : Failed to allocate memory for image (%u,%u,%u,%u).",
08654                                       cimg_instance,
08655                                       dx,dy,dz,dc);
08656         }
08657         fill(values,repeat_values);
08658       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
08659     }
08660 
08661     //! Construct an image from a raw memory buffer.
08662     /**
08663        This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dc) and fill its pixel buffer by
08664        copying data values from the input raw pixel buffer \p data_buffer.
08665     **/
08666     template<typename t>
08667     CImg(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1,
08668          const unsigned int dz=1, const unsigned int dc=1, const bool shared=false):_is_shared(false) {
08669       if (shared) {
08670         _width = _height = _depth = _spectrum = 0; _data = 0;
08671         throw CImgArgumentException(_cimg_instance
08672                                     "CImg() : Invalid construction request of a (%u,%u,%u,%u) shared instance from a (%s*) buffer "
08673                                     "(pixel types are different).",
08674                                     cimg_instance,
08675                                     dx,dy,dz,dc,CImg<t>::pixel_type());
08676       }
08677       const unsigned int siz = dx*dy*dz*dc;
08678       if (data_buffer && siz) {
08679         _width = dx; _height = dy; _depth = dz; _spectrum = dc;
08680         try { _data = new T[siz]; } catch (...) {
08681           _width = _height = _depth = _spectrum = 0; _data = 0;
08682           throw CImgInstanceException(_cimg_instance
08683                                       "CImg() : Failed to allocate memory for image (%u,%u,%u,%u).",
08684                                       cimg_instance,
08685                                       dx,dy,dz,dc);
08686 
08687         }
08688         const t *ptrs = data_buffer + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);
08689       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
08690     }
08691 
08692     CImg(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1,
08693          const unsigned int dz=1, const unsigned int dc=1, const bool shared=false) {
08694       const unsigned int siz = dx*dy*dz*dc;
08695       if (data_buffer && siz) {
08696         _width = dx; _height = dy; _depth = dz; _spectrum = dc; _is_shared = shared;
08697         if (_is_shared) _data = const_cast<T*>(data_buffer);
08698         else {
08699           try { _data = new T[siz]; } catch (...) {
08700             _width = _height = _depth = _spectrum = 0; _data = 0;
08701             throw CImgInstanceException(_cimg_instance
08702                                         "CImg() : Failed to allocate memory for image (%u,%u,%u,%u).",
08703                                         cimg_instance,
08704                                         dx,dy,dz,dc);
08705           }
08706           std::memcpy(_data,data_buffer,siz*sizeof(T)); }
08707       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
08708     }
08709 
08710     //! Construct an image from an image file.
08711     /**
08712        This constructor creates an instance image by reading it from a file.
08713        \param filename Filename of the image file.
08714        \remark
08715        - The image format is deduced from the filename only by looking for the filename extension i.e. without
08716        analyzing the file itself.
08717        - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with.
08718        More informations on this topic can be found in cimg_files_io.
08719        - If the filename is not found, a CImgIOException is thrown by this constructor.
08720     **/
08721     explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
08722       assign(filename);
08723     }
08724 
08725     //! Default copy constructor.
08726     /**
08727        The default copy constructor creates a new instance image having same dimensions
08728        (\ref width, \ref height, \ref depth, \ref _spectrum) and same pixel values as the input image \p img.
08729        \param img The input image to copy.
08730        \remark
08731        - If the input image \p img is non-shared or have a different template type \p t != \p T,
08732        the default copy constructor allocates a new pixel buffer and copy the pixel data
08733        of \p img into it. In this case, the pointers \ref data to the pixel buffers of the two images are different
08734        and the resulting instance image is non-shared.
08735        - If the input image \p img is shared and has the same template type \p t == \p T,
08736        the default copy constructor does not allocate a new pixel buffer and the resulting instance image
08737        shares its pixel buffer with the input image \p img, which means that modifying pixels of \p img also modifies
08738        the created instance image.
08739        - Copying an image having a different template type \p t != \p T performs a crude static cast conversion of each pixel value from
08740        type \p t to type \p T.
08741        - Copying an image having the same template type \p t == \p T is significantly faster.
08742     **/
08743     template<typename t>
08744     CImg(const CImg<t>& img):_is_shared(false) {
08745       const unsigned int siz = img.size();
08746       if (img._data && siz) {
08747         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
08748         try { _data = new T[siz]; } catch (...) {
08749           _width = _height = _depth = _spectrum = 0; _data = 0;
08750           throw CImgInstanceException(_cimg_instance
08751                                       "CImg() : Failed to allocate memory for image (%u,%u,%u,%u).",
08752                                       cimg_instance,
08753                                       img._width,img._height,img._depth,img._spectrum);
08754         }
08755         const t *ptrs = img._data + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);
08756       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
08757     }
08758 
08759     CImg(const CImg<T>& img) {
08760       const unsigned int siz = img.size();
08761       if (img._data && siz) {
08762         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = img._is_shared;
08763         if (_is_shared) _data = const_cast<T*>(img._data);
08764         else {
08765           try { _data = new T[siz]; } catch (...) {
08766             _width = _height = _depth = _spectrum = 0; _data = 0;
08767             throw CImgInstanceException(_cimg_instance
08768                                         "CImg() : Failed to allocate memory for image (%u,%u,%u,%u).",
08769                                         cimg_instance,
08770                                         img._width,img._height,img._depth,img._spectrum);
08771 
08772           }
08773           std::memcpy(_data,img._data,siz*sizeof(T));
08774         }
08775       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
08776     }
08777 
08778     //! Advanced copy constructor.
08779     /**
08780        The advanced copy constructor - as the default constructor CImg(const CImg< t >&) - creates a new instance image having same dimensions
08781        \ref width, \ref height, \ref depth, \ref spectrum and same pixel values as the input image \p img.
08782        But it also decides if the created instance image shares its memory with the input image \p img (if the input parameter
08783        \p shared is set to \p true) or not (if the input parameter \p shared is set to \p false).
08784        \param img The input image to copy.
08785        \param shared Boolean flag that decides if the copy is shared on non-shared.
08786        \remark
08787        - It is not possible to create a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T.
08788        - If a non-shared copy of the input image \p img is created, a new memory buffer is allocated for pixel data.
08789        - If a shared copy of the input image \p img is created, no extra memory is allocated and the pixel buffer of the instance
08790        image is the same as the one used by the input image \p img.
08791     **/
08792     template<typename t>
08793     CImg(const CImg<t>& img, const bool shared):_is_shared(false) {
08794       if (shared) {
08795         _width = _height = _depth = _spectrum = 0; _data = 0;
08796         throw CImgArgumentException(_cimg_instance
08797                                     "CImg() : Invalid construction request of a shared instance from a "
08798                                     "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
08799                                     cimg_instance,
08800                                     CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
08801       }
08802 
08803       const unsigned int siz = img.size();
08804       if (img._data && siz) {
08805         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
08806         try { _data = new T[siz]; } catch (...) {
08807           _width = _height = _depth = _spectrum = 0; _data = 0;
08808           throw CImgInstanceException(_cimg_instance
08809                                       "CImg() : Failed to allocate memory for image (%u,%u,%u,%u).",
08810                                       cimg_instance,
08811                                       img._width,img._height,img._depth,img._spectrum);
08812         }
08813         const t *ptrs = img._data + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);
08814       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
08815     }
08816 
08817     CImg(const CImg<T>& img, const bool shared) {
08818       const unsigned int siz = img.size();
08819       if (img._data && siz) {
08820         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = shared;
08821         if (_is_shared) _data = const_cast<T*>(img._data);
08822         else {
08823           try { _data = new T[siz]; } catch (...) {
08824             _width = _height = _depth = _spectrum = 0; _data = 0;
08825             throw CImgInstanceException(_cimg_instance
08826                                         "CImg() : Failed to allocate memory for image (%u,%u,%u,%u).",
08827                                         cimg_instance,
08828                                         img._width,img._height,img._depth,img._spectrum);
08829           }
08830           std::memcpy(_data,img._data,siz*sizeof(T));
08831         }
08832       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
08833     }
08834 
08835     //! Construct an image using dimensions of another image
08836     template<typename t>
08837     CImg(const CImg<t>& img, const char *const dimensions):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
08838       assign(img,dimensions);
08839     }
08840 
08841     //! Construct an image using dimensions of another image, and fill it with given values.
08842     template<typename t>
08843     CImg(const CImg<t>& img, const char *const dimensions, const T val):
08844       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
08845       assign(img,dimensions).fill(val);
08846     }
08847 
08848     //! Construct an image using dimensions of another image, and fill it with given values.
08849     template<typename t>
08850     CImg(const CImg<t>& img, const char *const dimensions, const char *const values, const bool repeat_values):
08851       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
08852       assign(img,dimensions).fill(values,repeat_values);
08853     }
08854 
08855     //! Construct an image from the content of a CImgDisplay instance.
08856     explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
08857       disp.snapshot(*this);
08858     }
08859 
08860     //! Return a shared version of the instance image.
08861     CImg<T> get_shared() {
08862       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
08863     }
08864 
08865     const CImg<T> get_shared() const {
08866       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
08867     }
08868 
08869     //! In-place version of the default constructor (STL-compliant name).
08870     /**
08871        This function is strictly equivalent to \ref assign() and has been
08872        introduced for having a STL-compatible function name.
08873     **/
08874     CImg<T>& clear() {
08875       return assign();
08876     }
08877 
08878     //! In-place version of the default constructor/destructor.
08879     /**
08880        This function replaces the instance image by an empty image.
08881        \remark
08882        - Memory used by the previous content of the instance image is freed if necessary.
08883        - If the instance image was initially shared, it is replaced by a (non-shared) empty image.
08884        - This function is useful to free memory used by an image that is not of use, but which
08885        has been created in the current code scope (i.e. not destroyed yet).
08886     **/
08887     CImg<T>& assign() {
08888       if (_data && !_is_shared) delete[] _data;
08889       _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
08890       return *this;
08891     }
08892 
08893     //! In-place version of the previous constructor.
08894     /**
08895        This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dc) with pixels of type \p T.
08896        \param dx Desired size along the X-axis, i.e. the \ref width of the image.
08897        \param dy Desired size along the Y-axis, i.e. the \ref height of the image.
08898        \param dz Desired size along the Z-axis, i.e. the \ref depth of the image.
08899        \param dc Desired size along the C-axis, i.e. the number of image channels \p _spectrum.
08900        - If one of the input dimension \p dx,\p dy,\p dz or \p dc is set to 0, the instance image becomes empty
08901        and all has its dimensions set to 0. No memory for pixel data is then allocated.
08902        - Memory buffer used to store previous pixel values is freed if necessary.
08903        - If the instance image is shared, this constructor actually does nothing more than verifying
08904        that new and old image dimensions fit.
08905        - Image pixels allocated by this function are \b not \b initialized.
08906        Use the function assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T)
08907        to assign an image of desired size with pixels set to a particular value.
08908     **/
08909     CImg<T>& assign(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dc=1) {
08910       const unsigned int siz = dx*dy*dz*dc;
08911       if (!siz) return assign();
08912       const unsigned int curr_siz = size();
08913       if (siz!=curr_siz) {
08914         if (_is_shared)
08915           throw CImgArgumentException(_cimg_instance
08916                                       "assign() : Invalid assignement request of shared instance from specified image (%u,%u,%u,%u).",
08917                                       cimg_instance,
08918                                       dx,dy,dz,dc);
08919         else {
08920           if (_data) delete[] _data;
08921           try { _data = new T[siz]; } catch (...) {
08922             _width = _height = _depth = _spectrum = 0; _data = 0;
08923             throw CImgInstanceException(_cimg_instance
08924                                         "assign() : Failed to allocate memory for image (%u,%u,%u,%u).",
08925                                         cimg_instance,
08926                                         dx,dy,dz,dc);
08927           }
08928         }
08929       }
08930       _width = dx; _height = dy; _depth = dz; _spectrum = dc;
08931       return *this;
08932     }
08933 
08934     //! In-place version of the previous constructor.
08935     /**
08936        This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dc) with pixels of type \p T
08937        and sets all pixel values of the instance image to \p val.
08938        \param dx Desired size along the X-axis, i.e. the \ref width of the image.
08939        \param dy Desired size along the Y-axis, i.e. the \ref height of the image.
08940        \param dz Desired size along the Z-axis, i.e. the \ref depth of the image.
08941        \param dc Desired size along the C-axis, i.e. the number of image channels \p _spectrum.
08942        \param val Default value for image pixels.
08943        \remark
08944        - This function has the same properties as assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int).
08945     **/
08946     CImg<T>& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc, const T val) {
08947       return assign(dx,dy,dz,dc).fill(val);
08948     }
08949 
08950     //! In-place version of the previous constructor.
08951     CImg<T>& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc,
08952                     const int val0, const int val1, ...) {
08953       assign(dx,dy,dz,dc);
08954       _CImg_stdarg(*this,val0,val1,dx*dy*dz*dc,int);
08955       return *this;
08956     }
08957 
08958     //! In-place version of the previous constructor.
08959     CImg<T>& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc,
08960                     const double val0, const double val1, ...) {
08961       assign(dx,dy,dz,dc);
08962       _CImg_stdarg(*this,val0,val1,dx*dy*dz*dc,double);
08963       return *this;
08964     }
08965 
08966     //! In-place version of the corresponding constructor.
08967     CImg<T>& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc,
08968                     const char *const values, const bool repeat_values) {
08969       return assign(dx,dy,dz,dc).fill(values,repeat_values);
08970     }
08971 
08972     //! In-place version of the previous constructor.
08973     template<typename t>
08974     CImg<T>& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1,
08975                     const unsigned int dz=1, const unsigned int dc=1) {
08976       const unsigned int siz = dx*dy*dz*dc;
08977       if (!data_buffer || !siz) return assign();
08978       assign(dx,dy,dz,dc);
08979       const t *ptrs = data_buffer + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);
08980       return *this;
08981     }
08982 
08983     CImg<T>& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1,
08984                     const unsigned int dz=1, const unsigned int dc=1) {
08985       const unsigned int siz = dx*dy*dz*dc;
08986       if (!data_buffer || !siz) return assign();
08987       const unsigned int curr_siz = size();
08988       if (data_buffer==_data && siz==curr_siz) return assign(dx,dy,dz,dc);
08989       if (_is_shared || data_buffer+siz<_data || data_buffer>=_data+size()) {
08990         assign(dx,dy,dz,dc);
08991         if (_is_shared) std::memmove(_data,data_buffer,siz*sizeof(T));
08992         else std::memcpy(_data,data_buffer,siz*sizeof(T));
08993       } else {
08994         T *new_data = 0;
08995         try { new_data = new T[siz]; } catch (...) {
08996           _width = _height = _depth = _spectrum = 0; _data = 0;
08997           throw CImgInstanceException(_cimg_instance
08998                                       "assign() : Failed to allocate memory for image (%u,%u,%u,%u).",
08999                                       cimg_instance,
09000                                       dx,dy,dz,dc);
09001         }
09002         std::memcpy(new_data,data_buffer,siz*sizeof(T));
09003         delete[] _data; _data = new_data; _width = dx; _height = dy; _depth = dz; _spectrum = dc;
09004       }
09005       return *this;
09006     }
09007 
09008     //! In-place version of the previous constructor, allowing to force the shared state of the instance image.
09009     template<typename t>
09010     CImg<T>& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy,
09011                     const unsigned int dz, const unsigned int dc, const bool shared) {
09012       if (shared)
09013         throw CImgArgumentException(_cimg_instance
09014                                     "assign() : Invalid assignment request of shared instance from (%s*) buffer"
09015                                     "(pixel types are different).",
09016                                     cimg_instance,
09017                                     CImg<t>::pixel_type());
09018 
09019       return assign(data_buffer,dx,dy,dz,dc);
09020     }
09021 
09022     CImg<T>& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy,
09023                     const unsigned int dz, const unsigned int dc, const bool shared) {
09024       const unsigned int siz = dx*dy*dz*dc;
09025       if (!data_buffer || !siz) {
09026         if (shared) throw CImgArgumentException(_cimg_instance
09027                                                 "assign() : Invalid assignment request of shared instance from (null) or empty buffer.",
09028                                                 cimg_instance);
09029         else return assign();
09030       }
09031       if (!shared) { if (_is_shared) assign(); assign(data_buffer,dx,dy,dz,dc); }
09032       else {
09033         if (!_is_shared) {
09034           if (data_buffer+siz<_data || data_buffer>=_data+size()) assign();
09035           else cimg::warn(_cimg_instance
09036                           "assign() : Shared instance image has overlapping memory.",
09037                           cimg_instance);
09038         }
09039         _width = dx; _height = dy; _depth = dz; _spectrum = dc; _is_shared = true;
09040         _data = const_cast<T*>(data_buffer);
09041       }
09042       return *this;
09043     }
09044 
09045     //! In-place version of the previous constructor.
09046     /**
09047        This function replaces the instance image by the one that have been read from the given file.
09048        \param filename Filename of the image file.
09049        - The image format is deduced from the filename only by looking for the filename extension i.e. without
09050        analyzing the file itself.
09051        - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with.
09052        More informations on this topic can be found in cimg_files_io.
09053        - If the filename is not found, a CImgIOException is thrown by this constructor.
09054     **/
09055     CImg<T>& assign(const char *const filename) {
09056       return load(filename);
09057     }
09058 
09059     //! In-place version of the default copy constructor.
09060     /**
09061        This function assigns a copy of the input image \p img to the current instance image.
09062        \param img The input image to copy.
09063        \remark
09064        - If the instance image is non-shared, the content of the input image \p img is copied into a new buffer
09065        becoming the new pixel buffer of the instance image, while the old pixel buffer is freed if necessary.
09066        - If the instance image is shared, the content of the input image \p img is copied into the current (shared) pixel buffer
09067        of the instance image, modifying then the image referenced by the shared instance image. The instance image still remains shared.
09068     **/
09069     template<typename t>
09070     CImg<T>& assign(const CImg<t>& img) {
09071       return assign(img._data,img._width,img._height,img._depth,img._spectrum);
09072     }
09073 
09074     //! In-place version of the advanced constructor.
09075     /**
09076        This function - as the simpler function assign(const CImg< t >&) - assigns a copy of the input image \p img to the
09077        current instance image. But it also decides if the copy is shared (if the input parameter \p shared is set to \c true)
09078        or non-shared (if the input parameter \p shared is set to \c false).
09079        \param img The input image to copy.
09080        \param shared Boolean flag that decides if the copy is shared or non-shared.
09081        \remark
09082        - It is not possible to assign a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T.
09083        - If a non-shared copy of the input image \p img is assigned, a new memory buffer is allocated for pixel data.
09084        - If a shared copy of the input image \p img is assigned, no extra memory is allocated and the pixel buffer of the instance
09085        image is the same as the one used by the input image \p img.
09086     **/
09087     template<typename t>
09088     CImg<T>& assign(const CImg<t>& img, const bool shared) {
09089       return assign(img._data,img._width,img._height,img._depth,img._spectrum,shared);
09090     }
09091 
09092     //! In-place version of the previous constructor.
09093     template<typename t>
09094     CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
09095       if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
09096       unsigned int siz[4] = { 0,1,1,1 }, k = 0;
09097       for (const char *s = dimensions; *s && k<4; ++k) {
09098         char item[256] = { 0 };
09099         if (std::sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item)>0) s+=std::strlen(item);
09100         if (*s) {
09101           unsigned int val = 0; char sep = 0;
09102           if (std::sscanf(s,"%u%c",&val,&sep)>0) {
09103             if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
09104             else siz[k] = val;
09105             while (*s>='0' && *s<='9') ++s; if (sep=='%') ++s;
09106           } else switch (cimg::uncase(*s)) {
09107           case 'x' : case 'w' : siz[k] = img._width; ++s; break;
09108           case 'y' : case 'h' : siz[k] = img._height; ++s; break;
09109           case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
09110           case 'v' : case 'c' : siz[k] = img._spectrum; ++s; break;
09111           default :
09112             throw CImgArgumentException(_cimg_instance
09113                                         "assign() : Invalid character '%c' detected in specified dimension string '%s'.",
09114                                         cimg_instance,
09115                                         *s,dimensions);
09116           }
09117         }
09118       }
09119       return assign(siz[0],siz[1],siz[2],siz[3]);
09120     }
09121 
09122     //! In-place version of the previous constructor.
09123     template<typename t>
09124     CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T val) {
09125       return assign(img,dimensions).fill(val);
09126     }
09127 
09128     //! In-place version of the previous constructor.
09129     template<typename t>
09130     CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const char *const values, const bool repeat_values) {
09131       return assign(img,dimensions).fill(values,repeat_values);
09132     }
09133 
09134     //! In-place version of the previous constructor.
09135     CImg<T>& assign(const CImgDisplay &disp) {
09136       disp.snapshot(*this);
09137       return *this;
09138     }
09139 
09140     //! Move the content of the instance image into another one in a way that memory copies are avoided if possible.
09141     /**
09142        The instance image is always empty after a call to this function.
09143     **/
09144     template<typename t>
09145     CImg<t>& move_to(CImg<t>& img) {
09146       img.assign(*this);
09147       assign();
09148       return img;
09149     }
09150 
09151     CImg<T>& move_to(CImg<T>& img) {
09152       if (_is_shared || img._is_shared) { img.assign(*this); assign(); } else { img.assign(); swap(img); }
09153       return img;
09154     }
09155 
09156     template<typename t>
09157     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
09158       const unsigned int npos = pos>list._width?list._width:pos;
09159       move_to(list.insert(1,npos)[npos]);
09160       return list;
09161     }
09162 
09163     //! Return a reference to an empty image.
09164     static CImg<T>& empty() {
09165       static CImg<T> _empty;
09166       return _empty.assign();
09167     }
09168 
09169     //! Swap all fields of two images. Use with care !
09170     CImg<T>& swap(CImg<T>& img) {
09171       cimg::swap(_width,img._width);
09172       cimg::swap(_height,img._height);
09173       cimg::swap(_depth,img._depth);
09174       cimg::swap(_spectrum,img._spectrum);
09175       cimg::swap(_data,img._data);
09176       cimg::swap(_is_shared,img._is_shared);
09177       return img;
09178     }
09179 
09180     //@}
09181     //------------------------------------------
09182     //
09183     //! \name Overloaded Operators
09184     //@{
09185     //------------------------------------------
09186 
09187     //! Fast access to pixel value for reading or writing.
09188     /**
09189        \param x X-coordinate of the pixel.
09190        \param y Y-coordinate of the pixel.
09191        \param z Z-coordinate of the pixel.
09192        \param v C-coordinate of the pixel.
09193 
09194        - If one image dimension is equal to 1, it can be omitted in the coordinate list (see example below).
09195        - If the macro \c 'cimg_verbosity'>=3, boundary checking is performed and warning messages may appear
09196        (but function performances decrease).
09197 
09198        \par example:
09199        \code
09200        CImg<float> img(100,100,1,3,0);                       // Define a 100x100 color image with float-valued black pixels.
09201        const float valR = img(10,10,0,0);                    // Read the red component at coordinates (10,10).
09202        const float valG = img(10,10,0,1);                    // Read the green component at coordinates (10,10)
09203        const float valB = img(10,10,2);                      // Read the blue component at coordinates (10,10) (Z-coordinate omitted here).
09204        const float avg = (valR + valG + valB)/3;             // Compute average pixel value.
09205        img(10,10,0) = img(10,10,1) = img(10,10,2) = avg;     // Replace the pixel (10,10) by the average grey value.
09206        \endcode
09207     **/
09208 #if cimg_verbosity>=3
09209     T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
09210       const unsigned int off = (unsigned int)offset(x,y,z,c);
09211       if (!_data || off>=size()) {
09212         cimg::warn(_cimg_instance
09213                    "operator() : Invalid pixel request, at coordinates (%u,%u,%u,%u) [offset=%u].",
09214                    cimg_instance,
09215                    x,y,z,c,off);
09216         return *_data;
09217       }
09218       else return _data[off];
09219     }
09220 
09221     const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
09222       return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
09223     }
09224 
09225 #else
09226     T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
09227       return _data[x + y*_width + z*_width*_height + c*_width*_height*_depth];
09228     }
09229 
09230     const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
09231       return _data[x + y*_width + z*_width*_height + c*_width*_height*_depth];
09232     }
09233 #endif
09234 
09235     //! Return address of the pixel buffer.
09236     operator const T*() const {
09237       return _data;
09238     }
09239 
09240     operator T*() {
09241       return _data;
09242     }
09243 
09244     //! Operator=().
09245     /**
09246        Assignment operator. Fill all pixels of the instance image with the same value.
09247        The image size is not modified.
09248     **/
09249     CImg<T>& operator=(const T val) {
09250       return fill(val);
09251     }
09252 
09253     //! Operator=().
09254     /**
09255        Assignment operator.
09256        If \p expression is a formula or a list of values, the image pixels are filled
09257        according to the expression and the image size is not modified.
09258        If \p expression is a filename, the image is replaced by the input file data
09259        (so image size is modified).
09260     **/
09261     CImg<T>& operator=(const char *const expression) {
09262       const unsigned int omode = cimg::exception_mode();
09263       cimg::exception_mode() = 0;
09264       try {
09265         fill(expression,true);
09266       } catch (CImgException&) {
09267         cimg::exception_mode() = omode;
09268         load(expression);
09269       }
09270       cimg::exception_mode() = omode;
09271       return *this;
09272     }
09273 
09274     //! Operator=().
09275     /**
09276        Assignement operator.
09277        If instance image is non-shared, replace the instance image by a copy of the argument image.
09278        If instance image is shared, replace the image content by the content of the argument image.
09279     **/
09280     template<typename t>
09281     CImg<T>& operator=(const CImg<t>& img) {
09282       return assign(img);
09283     }
09284 
09285     CImg<T>& operator=(const CImg<T>& img) {
09286       return assign(img);
09287     }
09288 
09289     //! Operator=().
09290     CImg<T>& operator=(const CImgDisplay& disp) {
09291       disp.snapshot(*this);
09292       return *this;
09293     }
09294 
09295     //! Operator+=().
09296     template<typename t>
09297     CImg<T>& operator+=(const t val) {
09298       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd + val);
09299       return *this;
09300     }
09301 
09302     //! Operator+=().
09303     CImg<T>& operator+=(const char *const expression) {
09304       const unsigned int omode = cimg::exception_mode();
09305       cimg::exception_mode() = 0;
09306       try {
09307         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09308         _cimg_math_parser mp(base,expression,"operator+=");
09309         T *ptrd = _data;
09310         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp.eval(x,y,z,c)); ++ptrd; }
09311       } catch (CImgException&) {
09312         cimg::exception_mode() = omode;
09313         CImg<T> values(_width,_height,_depth,_spectrum);
09314         values = expression;
09315         *this+=values;
09316       }
09317       cimg::exception_mode() = omode;
09318       return *this;
09319     }
09320 
09321     //! Operator+=().
09322     template<typename t>
09323     CImg<T>& operator+=(const CImg<t>& img) {
09324       const unsigned int siz = size(), isiz = img.size();
09325       if (siz && isiz) {
09326         if (is_overlapped(img)) return *this+=+img;
09327         T *ptrd = _data, *const ptre = _data + siz;
09328         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
09329           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
09330         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
09331       }
09332       return *this;
09333     }
09334 
09335     //! Operator++() (prefix).
09336     CImg<T>& operator++() {
09337       cimg_for(*this,ptrd,T) ++*ptrd;
09338       return *this;
09339     }
09340 
09341     //! Operator++() (postfix).
09342     CImg<T> operator++(int) {
09343       const CImg<T> copy(*this,false);
09344       ++*this;
09345       return copy;
09346     }
09347 
09348     //! Operator+() (unary).
09349     /**
09350        \remark
09351        - This operator always returns a non-shared copy of an image.
09352     **/
09353     CImg<T> operator+() const {
09354       return CImg<T>(*this,false);
09355     }
09356 
09357     //! Operator+().
09358     template<typename t>
09359     CImg<_cimg_Tt> operator+(const t val) const {
09360       return CImg<_cimg_Tt>(*this,false)+=val;
09361     }
09362 
09363     //! Operator+().
09364     CImg<Tfloat> operator+(const char *const expression) const {
09365       return CImg<Tfloat>(*this,false)+=expression;
09366     }
09367 
09368     //! Operator+().
09369     template<typename t>
09370     CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
09371       return CImg<_cimg_Tt>(*this,false)+=img;
09372     }
09373 
09374     //! Operator-=().
09375     template<typename t>
09376     CImg<T>& operator-=(const t val) {
09377       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd - val);
09378       return *this;
09379     }
09380 
09381     //! Operator-=().
09382     CImg<T>& operator-=(const char *const expression) {
09383       const unsigned int omode = cimg::exception_mode();
09384       cimg::exception_mode() = 0;
09385       try {
09386         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09387         _cimg_math_parser mp(base,expression,"operator-=");
09388         T *ptrd = _data;
09389         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp.eval(x,y,z,c)); ++ptrd; }
09390       } catch (CImgException&) {
09391         cimg::exception_mode() = omode;
09392         CImg<T> values(_width,_height,_depth,_spectrum);
09393         values = expression;
09394         *this-=values;
09395       }
09396       cimg::exception_mode() = omode;
09397       return *this;
09398     }
09399 
09400     //! Operator-=().
09401     template<typename t>
09402     CImg<T>& operator-=(const CImg<t>& img) {
09403       const unsigned int siz = size(), isiz = img.size();
09404       if (siz && isiz) {
09405         if (is_overlapped(img)) return *this-=+img;
09406         T *ptrd = _data, *const ptre = _data + siz;
09407         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
09408           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
09409         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
09410       }
09411       return *this;
09412     }
09413 
09414     //! Operator--() (prefix).
09415     CImg<T>& operator--() {
09416       cimg_for(*this,ptrd,T) *ptrd = *ptrd-(T)1;
09417       return *this;
09418     }
09419 
09420     //! Operator--() (postfix).
09421     CImg<T> operator--(int) {
09422       const CImg<T> copy(*this,false);
09423       --*this;
09424       return copy;
09425     }
09426 
09427     //! Operator-() (unary).
09428     CImg<T> operator-() const {
09429       return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
09430     }
09431 
09432     //! Operator-().
09433     template<typename t>
09434     CImg<_cimg_Tt> operator-(const t val) const {
09435       return CImg<_cimg_Tt>(*this,false)-=val;
09436     }
09437 
09438     //! Operator-().
09439     CImg<Tfloat> operator-(const char *const expression) const {
09440       return CImg<Tfloat>(*this,false)-=expression;
09441     }
09442 
09443     //! Operator-().
09444     template<typename t>
09445     CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
09446       return CImg<_cimg_Tt>(*this,false)-=img;
09447     }
09448 
09449     //! Operator*=().
09450     template<typename t>
09451     CImg<T>& operator*=(const t val) {
09452       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd * val);
09453       return *this;
09454     }
09455 
09456     //! Operator*=().
09457     CImg<T>& operator*=(const char *const expression) {
09458       const unsigned int omode = cimg::exception_mode();
09459       cimg::exception_mode() = 0;
09460       try {
09461         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09462         _cimg_math_parser mp(base,expression,"operator*=");
09463         T *ptrd = _data;
09464         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp.eval(x,y,z,c)); ++ptrd; }
09465       } catch (CImgException&) {
09466         cimg::exception_mode() = omode;
09467         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
09468         values = expression;
09469         *this*=values;
09470       }
09471       cimg::exception_mode() = omode;
09472       return *this;
09473     }
09474 
09475     //! Operator*=().
09476     template<typename t>
09477     CImg<T>& operator*=(const CImg<t>& img) {
09478       return ((*this)*img).move_to(*this);
09479     }
09480 
09481     //! Operator*().
09482     template<typename t>
09483     CImg<_cimg_Tt> operator*(const t val) const {
09484       return CImg<_cimg_Tt>(*this,false)*=val;
09485     }
09486 
09487     //! Operator*().
09488     CImg<Tfloat> operator*(const char *const expression) const {
09489       return CImg<Tfloat>(*this,false)*=expression;
09490     }
09491 
09492     //! Operator*().
09493     template<typename t>
09494     CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
09495       if (_width!=img._height || _depth!=1 || _spectrum!=1)
09496         throw CImgArgumentException(_cimg_instance
09497                                     "operator*() : Invalid multiplication of instance by specified matrix (%u,%u,%u,%u,%p)",
09498                                     cimg_instance,
09499                                     img._width,img._height,img._depth,img._spectrum,img._data);
09500 
09501       CImg<_cimg_Tt> res(img._width,_height);
09502       _cimg_Tt val, *ptrd = res._data;
09503 #ifdef cimg_use_openmp
09504 #pragma omp parallel for if (size()>=1000 && img.size()>=1000) private(val)
09505 #endif
09506       cimg_forXY(res,i,j) { val = 0; cimg_forX(*this,k) val+=(*this)(k,j)*img(i,k); *(ptrd++) = val; }
09507       return res;
09508     }
09509 
09510     //! Operator/=().
09511     template<typename t>
09512     CImg<T>& operator/=(const t val) {
09513       cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd / val);
09514       return *this;
09515     }
09516 
09517     //! Operator/=().
09518     CImg<T>& operator/=(const char *const expression) {
09519       const unsigned int omode = cimg::exception_mode();
09520       cimg::exception_mode() = 0;
09521       try {
09522         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09523         _cimg_math_parser mp(base,expression,"operator/=");
09524         T *ptrd = _data;
09525         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp.eval(x,y,z,c)); ++ptrd; }
09526       } catch (CImgException&) {
09527         cimg::exception_mode() = omode;
09528         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
09529         values = expression;
09530         *this/=values;
09531       }
09532       cimg::exception_mode() = omode;
09533       return *this;
09534     }
09535 
09536     //! Operator/=().
09537     template<typename t>
09538     CImg<T>& operator/=(const CImg<t>& img) {
09539       return (*this*img.get_invert()).move_to(*this);
09540     }
09541 
09542     //! Operator/().
09543     template<typename t>
09544     CImg<_cimg_Tt> operator/(const t val) const {
09545       return CImg<_cimg_Tt>(*this,false)/=val;
09546     }
09547 
09548     //! Operator/().
09549     CImg<Tfloat> operator/(const char *const expression) const {
09550       return CImg<Tfloat>(*this,false)/=expression;
09551     }
09552 
09553     //! Operator/().
09554     template<typename t>
09555     CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
09556       return (*this)*img.get_invert();
09557     }
09558 
09559     //! Operator%=().
09560     template<typename t>
09561     CImg<T>& operator%=(const t val) {
09562       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)val);
09563       return *this;
09564     }
09565 
09566     //! Operator%=().
09567     CImg<T>& operator%=(const char *const expression) {
09568       const unsigned int omode = cimg::exception_mode();
09569       cimg::exception_mode() = 0;
09570       try {
09571         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09572         _cimg_math_parser mp(base,expression,"operator%=");
09573         T *ptrd = _data;
09574         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
09575       } catch (CImgException&) {
09576         cimg::exception_mode() = omode;
09577         CImg<T> values(_width,_height,_depth,_spectrum);
09578         values = expression;
09579         *this%=values;
09580       }
09581       cimg::exception_mode() = omode;
09582       return *this;
09583     }
09584 
09585     //! Operator%=().
09586     template<typename t>
09587     CImg<T>& operator%=(const CImg<t>& img) {
09588       const unsigned int siz = size(), isiz = img.size();
09589       if (siz && isiz) {
09590         if (is_overlapped(img)) return *this%=+img;
09591         T *ptrd = _data, *const ptre = _data + siz;
09592         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
09593           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
09594         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
09595       }
09596       return *this;
09597     }
09598 
09599     //! Operator%().
09600     template<typename t>
09601     CImg<_cimg_Tt> operator%(const t val) const {
09602       return CImg<_cimg_Tt>(*this,false)%=val;
09603     }
09604 
09605     //! Operator%().
09606     CImg<Tfloat> operator%(const char *const expression) const {
09607       return CImg<Tfloat>(*this,false)%=expression;
09608     }
09609 
09610     //! Operator%().
09611     template<typename t>
09612     CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
09613       return CImg<_cimg_Tt>(*this,false)%=img;
09614     }
09615 
09616     //! Operator&=().
09617     template<typename t>
09618     CImg<T>& operator&=(const t val) {
09619       cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)val);
09620       return *this;
09621     }
09622 
09623     //! Operator&=().
09624     CImg<T>& operator&=(const char *const expression) {
09625       const unsigned int omode = cimg::exception_mode();
09626       cimg::exception_mode() = 0;
09627       try {
09628         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09629         _cimg_math_parser mp(base,expression,"operator&=");
09630         T *ptrd = _data;
09631         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
09632       } catch (CImgException&) {
09633         cimg::exception_mode() = omode;
09634         CImg<T> values(_width,_height,_depth,_spectrum);
09635         values = expression;
09636         *this&=values;
09637       }
09638       cimg::exception_mode() = omode;
09639       return *this;
09640     }
09641 
09642     //! Operator&=().
09643     template<typename t>
09644     CImg<T>& operator&=(const CImg<t>& img) {
09645       const unsigned int siz = size(), isiz = img.size();
09646       if (siz && isiz) {
09647         if (is_overlapped(img)) return *this&=+img;
09648         T *ptrd = _data, *const ptre = _data + siz;
09649         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
09650           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
09651         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
09652       }
09653       return *this;
09654     }
09655 
09656     //! Operator&().
09657     template<typename t>
09658     CImg<T> operator&(const t val) const {
09659       return (+*this)&=val;
09660     }
09661 
09662     //! Operator|=().
09663     template<typename t>
09664     CImg<T>& operator|=(const t val) {
09665       cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)val);
09666       return *this;
09667     }
09668 
09669     //! Operator|=().
09670     CImg<T>& operator|=(const char *const expression) {
09671       const unsigned int omode = cimg::exception_mode();
09672       cimg::exception_mode() = 0;
09673       try {
09674         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09675         _cimg_math_parser mp(base,expression,"operator|=");
09676         T *ptrd = _data;
09677         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
09678       } catch (CImgException&) {
09679         cimg::exception_mode() = omode;
09680         CImg<T> values(_width,_height,_depth,_spectrum);
09681         values = expression;
09682         *this|=values;
09683       }
09684       cimg::exception_mode() = omode;
09685       return *this;
09686     }
09687 
09688     //! Operator|=().
09689     template<typename t>
09690     CImg<T>& operator|=(const CImg<t>& img) {
09691       const unsigned int siz = size(), isiz = img.size();
09692       if (siz && isiz) {
09693         if (is_overlapped(img)) return *this|=+img;
09694         T *ptrd = _data, *const ptre = _data + siz;
09695         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
09696           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
09697         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
09698       }
09699       return *this;
09700     }
09701 
09702     //! Operator|().
09703     template<typename t>
09704     CImg<T> operator|(const t val) const {
09705       return (+*this)|=val;
09706     }
09707 
09708     //! Operator^=().
09709     template<typename t>
09710     CImg<T>& operator^=(const t val) {
09711       cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)val);
09712       return *this;
09713     }
09714 
09715     //! Operator^=().
09716     CImg<T>& operator^=(const char *const expression) {
09717       const unsigned int omode = cimg::exception_mode();
09718       cimg::exception_mode() = 0;
09719       try {
09720         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09721         _cimg_math_parser mp(base,expression,"operator^=");
09722         T *ptrd = _data;
09723         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
09724       } catch (CImgException&) {
09725         cimg::exception_mode() = omode;
09726         CImg<T> values(_width,_height,_depth,_spectrum);
09727         values = expression;
09728         *this^=values;
09729       }
09730       cimg::exception_mode() = omode;
09731       return *this;
09732     }
09733 
09734     //! Operator^=().
09735     template<typename t>
09736     CImg<T>& operator^=(const CImg<t>& img) {
09737       const unsigned int siz = size(), isiz = img.size();
09738       if (siz && isiz) {
09739         if (is_overlapped(img)) return *this^=+img;
09740         T *ptrd = _data, *const ptre = _data + siz;
09741         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
09742           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
09743         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
09744       }
09745       return *this;
09746     }
09747 
09748     //! Operator^().
09749     template<typename t>
09750     CImg<T> operator^(const t val) const {
09751       return (+*this)^=val;
09752     }
09753 
09754     //! Operator<<=().
09755     template<typename t>
09756     CImg<T>& operator<<=(const t val) {
09757       cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) << (int)val);
09758       return *this;
09759     }
09760 
09761     //! Operator<<=().
09762     CImg<T>& operator<<=(const char *const expression) {
09763       const unsigned int omode = cimg::exception_mode();
09764       cimg::exception_mode() = 0;
09765       try {
09766         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09767         _cimg_math_parser mp(base,expression,"operator<<=");
09768         T *ptrd = _data;
09769         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp.eval(x,y,z,c)); ++ptrd; }
09770       } catch (CImgException&) {
09771         cimg::exception_mode() = omode;
09772         CImg<T> values(_width,_height,_depth,_spectrum);
09773         values = expression;
09774         *this<<=values;
09775       }
09776       cimg::exception_mode() = omode;
09777       return *this;
09778     }
09779 
09780     //! Operator<<=().
09781     template<typename t>
09782     CImg<T>& operator<<=(const CImg<t>& img) {
09783       const unsigned int siz = size(), isiz = img.size();
09784       if (siz && isiz) {
09785         if (is_overlapped(img)) return *this^=+img;
09786         T *ptrd = _data, *const ptre = _data + siz;
09787         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
09788           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
09789         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
09790       }
09791       return *this;
09792     }
09793 
09794     //! Operator<<().
09795     template<typename t>
09796     CImg<T> operator<<(const t val) const {
09797       return (+*this)<<=val;
09798     }
09799 
09800     //! Operator>>=().
09801     template<typename t>
09802     CImg<T>& operator>>=(const t val) {
09803       cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) >> (int)val);
09804       return *this;
09805     }
09806 
09807     //! Operator>>=().
09808     CImg<T>& operator>>=(const char *const expression) {
09809       const unsigned int omode = cimg::exception_mode();
09810       cimg::exception_mode() = 0;
09811       try {
09812         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
09813         _cimg_math_parser mp(base,expression,"operator<<=");
09814         T *ptrd = _data;
09815         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp.eval(x,y,z,c)); ++ptrd; }
09816       } catch (CImgException&) {
09817         cimg::exception_mode() = omode;
09818         CImg<T> values(_width,_height,_depth,_spectrum);
09819         values = expression;
09820         *this>>=values;
09821       }
09822       cimg::exception_mode() = omode;
09823       return *this;
09824     }
09825 
09826     //! Operator>>=().
09827     template<typename t>
09828     CImg<T>& operator>>=(const CImg<t>& img) {
09829       const unsigned int siz = size(), isiz = img.size();
09830       if (siz && isiz) {
09831         if (is_overlapped(img)) return *this^=+img;
09832         T *ptrd = _data, *const ptre = _data + siz;
09833         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
09834           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
09835         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
09836       }
09837       return *this;
09838     }
09839 
09840     //! Operator>>().
09841     template<typename t>
09842     CImg<T> operator>>(const t val) const {
09843       return (+*this)>>=val;
09844     }
09845 
09846     //! Operator==().
09847     template<typename t>
09848     bool operator==(const CImg<t>& img) const {
09849       const unsigned int siz = size();
09850       bool vequal = true;
09851       if (siz!=img.size()) return false;
09852       t *ptrs = img._data + siz;
09853       for (T *ptrd = _data + siz; vequal && ptrd>_data; vequal = vequal && ((*(--ptrd))==(*(--ptrs)))) {}
09854       return vequal;
09855     }
09856 
09857     //! Operator!=().
09858     template<typename t>
09859     bool operator!=(const CImg<t>& img) const {
09860       return !((*this)==img);
09861     }
09862 
09863     //! Operator,().
09864     template<typename t>
09865     CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
09866       return CImgList<_cimg_Tt>(*this,img);
09867     }
09868 
09869     //! Operator,().
09870     template<typename t>
09871     CImgList<_cimg_Tt> operator,(CImgList<t>& list) const {
09872       return CImgList<_cimg_Tt>(list).insert(*this,0);
09873     }
09874 
09875     //! Operator<().
09876     CImgList<T> operator<(const char axis) const {
09877       return get_split(axis);
09878     }
09879 
09880     //! Operator~().
09881     CImg<T> operator~() const {
09882       CImg<T> res(_width,_height,_depth,_spectrum);
09883       const T *ptrs = end();
09884       cimg_for(res,ptrd,T) { const unsigned long val = (unsigned long)*(--ptrs); *ptrd = (T)~val; }
09885       return res;
09886     }
09887 
09888     //@}
09889     //-------------------------------------
09890     //
09891     //! \name Instance Characteristics
09892     //@{
09893     //-------------------------------------
09894 
09895     //! Return the type of the pixel values.
09896     /**
09897        \return a string describing the type of the image pixels (template parameter \p T).
09898        - The string returned may contains spaces (<tt>"unsigned char"</tt>).
09899        - If the template parameter T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
09900     **/
09901     static const char* pixel_type() {
09902       return cimg::type<T>::string();
09903     }
09904 
09905     //! Return the number of columns of the instance image (size along the X-axis, i.e image width).
09906     int width() const {
09907       return (int)_width;
09908     }
09909 
09910     //! Return the number of rows of the instance image (size along the Y-axis, i.e image height).
09911     int height() const {
09912       return (int)_height;
09913     }
09914 
09915     //! Return the number of slices of the instance image (size along the Z-axis).
09916     int depth() const {
09917       return (int)_depth;
09918     }
09919 
09920     //! Return the number of vector channels of the instance image (size along the C-axis).
09921     int spectrum() const {
09922       return (int)_spectrum;
09923     }
09924 
09925     //! Return the number of image buffer elements.
09926     /**
09927        - Equivalent to : width() * height() * depth() * spectrum().
09928 
09929        \par example:
09930        \code
09931        CImg<> img(100,100,1,3);
09932        if (img.size()==100*100*3) std::fprintf(stderr,"This statement is true");
09933        \endcode
09934     **/
09935     unsigned int size() const {
09936       return _width*_height*_depth*_spectrum;
09937     }
09938 
09939     //! Return a pointer to the pixel buffer.
09940     T* data() {
09941       return _data;
09942     }
09943 
09944     const T* data() const {
09945       return _data;
09946     }
09947 
09948     //! Return a pointer to the pixel value located at (\p x,\p y,\p z,\p v).
09949     /**
09950        \param x X-coordinate of the pixel.
09951        \param y Y-coordinate of the pixel.
09952        \param z Z-coordinate of the pixel.
09953        \param v C-coordinate of the pixel.
09954 
09955        - When called without parameters, data() returns a pointer to the begining of the pixel buffer.
09956        - If the macro \c 'cimg_verbosity'>=3, boundary checking is performed and warning messages may appear if
09957        given coordinates are outside the image range (but function performances decrease).
09958 
09959        \par example:
09960        \code
09961        CImg<float> img(100,100,1,1,0);   // Define a 100x100 greyscale image with float-valued pixels.
09962        float *ptr = data(10,10);         // Get a pointer to the pixel located at (10,10).
09963        float val = *ptr;                 // Get the pixel value.
09964        \endcode
09965     **/
09966 #if cimg_verbosity>=3
09967     T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
09968       const unsigned int off = (unsigned int)offset(x,y,z,c);
09969       if (off>=size()) {
09970         cimg::warn(_cimg_instance
09971                    "data() : Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
09972                    cimg_instance,
09973                    x,y,z,c,off);
09974         return _data;
09975       }
09976       return _data + off;
09977     }
09978 
09979     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
09980       return const_cast<CImg<T>*>(this)->data(x,y,z,c);
09981     }
09982 #else
09983     T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
09984       return _data + x + y*_width + z*_width*_height + c*_width*_height*_depth;
09985     }
09986 
09987     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
09988       return _data + x + y*_width + z*_width*_height + c*_width*_height*_depth;
09989     }
09990 #endif
09991 
09992     //! Return the offset of the pixel coordinates (\p x,\p y,\p z,\p v) with respect to the data pointer \c data.
09993     /**
09994        \param x X-coordinate of the pixel.
09995        \param y Y-coordinate of the pixel.
09996        \param z Z-coordinate of the pixel.
09997        \param v C-coordinate of the pixel.
09998 
09999        - No checking is done on the validity of the given coordinates.
10000 
10001        \par Example:
10002        \code
10003        CImg<float> img(100,100,1,3,0);         // Define a 100x100 color image with float-valued black pixels.
10004        long off = img.offset(10,10,0,2);       // Get the offset of the blue value of the pixel located at (10,10).
10005        float val = img[off];                   // Get the blue value of the pixel.
10006        \endcode
10007     **/
10008     int offset(const int x, const int y=0, const int z=0, const int c=0) const {
10009       return x + y*_width + z*_width*_height + c*_width*_height*_depth;
10010     }
10011 
10012     //! Return an iterator to the first image pixel
10013     iterator begin() {
10014       return _data;
10015     }
10016 
10017     const_iterator begin() const {
10018       return _data;
10019     }
10020 
10021     //! Return an iterator pointing after the last image pixel (STL-compliant name).
10022     iterator end() {
10023       return _data + size();
10024     }
10025 
10026     const_iterator end() const {
10027       return _data + size();
10028     }
10029 
10030     //! Return reference to the first image pixel (STL-compliant name).
10031     const T& front() const {
10032       return *_data;
10033     }
10034 
10035     T& front() {
10036       return *_data;
10037     }
10038 
10039     //! Return a reference to the last image pixel (STL-compliant name).
10040     const T& back() const {
10041       return *(_data + size() - 1);
10042     }
10043 
10044     T& back() {
10045       return *(_data + size() - 1);
10046     }
10047 
10048     //! Read a pixel value with Dirichlet boundary conditions.
10049     T& at(const int off, const T out_val) {
10050       return (off<0 || off>=(int)size())?(cimg::temporary(out_val)=out_val):(*this)[off];
10051     }
10052 
10053     T at(const int off, const T out_val) const {
10054       return (off<0 || off>=(int)size())?out_val:(*this)[off];
10055     }
10056 
10057     //! Read a pixel value with Neumann boundary conditions.
10058     T& at(const int off) {
10059       if (is_empty())
10060         throw CImgInstanceException(_cimg_instance
10061                                     "at() : Empty instance.",
10062                                     cimg_instance);
10063       return _at(off);
10064     }
10065 
10066     T at(const int off) const {
10067       if (is_empty())
10068         throw CImgInstanceException(_cimg_instance
10069                                     "at() : Empty instance.",
10070                                     cimg_instance);
10071       return _at(off);
10072     }
10073 
10074     T& _at(const int off) {
10075       const unsigned int siz = (unsigned int)size();
10076       return (*this)[off<0?0:(unsigned int)off>=siz?siz-1:off];
10077     }
10078 
10079     T _at(const int off) const {
10080       const unsigned int siz = (unsigned int)size();
10081       return (*this)[off<0?0:(unsigned int)off>=siz?siz-1:off];
10082     }
10083 
10084     //! Read a pixel value with Dirichlet boundary conditions for the first coordinates (\c x).
10085     T& atX(const int x, const int y, const int z, const int c, const T out_val) {
10086       return (x<0 || x>=width())?(cimg::temporary(out_val)=out_val):(*this)(x,y,z,c);
10087     }
10088 
10089     T atX(const int x, const int y, const int z, const int c, const T out_val) const {
10090       return (x<0 || x>=width())?out_val:(*this)(x,y,z,c);
10091     }
10092 
10093     //! Read a pixel value with Neumann boundary conditions for the first coordinates (\c x).
10094     T& atX(const int x, const int y=0, const int z=0, const int c=0) {
10095       if (is_empty())
10096         throw CImgInstanceException(_cimg_instance
10097                                     "atX() : Empty instance.",
10098                                     cimg_instance);
10099       return _atX(x,y,z,c);
10100     }
10101 
10102     T atX(const int x, const int y=0, const int z=0, const int c=0) const {
10103       if (is_empty())
10104         throw CImgInstanceException(_cimg_instance
10105                                     "atX() : Empty instance.",
10106                                     cimg_instance);
10107       return _atX(x,y,z,c);
10108     }
10109 
10110     T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
10111       return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
10112     }
10113 
10114     T _atX(const int x, const int y=0, const int z=0, const int c=0) const {
10115       return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
10116     }
10117 
10118     //! Read a pixel value with Dirichlet boundary conditions for the two first coordinates (\c x,\c y).
10119     T& atXY(const int x, const int y, const int z, const int c, const T out_val) {
10120       return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_val)=out_val):(*this)(x,y,z,c);
10121     }
10122 
10123     T atXY(const int x, const int y, const int z, const int c, const T out_val) const {
10124       return (x<0 || y<0 || x>=width() || y>=height())?out_val:(*this)(x,y,z,c);
10125     }
10126 
10127     //! Read a pixel value with Neumann boundary conditions for the two first coordinates (\c x,\c y).
10128     T& atXY(const int x, const int y, const int z=0, const int c=0) {
10129       if (is_empty())
10130         throw CImgInstanceException(_cimg_instance
10131                                     "atXY() : Empty instance.",
10132                                     cimg_instance);
10133       return _atXY(x,y,z,c);
10134     }
10135 
10136     T atXY(const int x, const int y, const int z=0, const int c=0) const {
10137       if (is_empty())
10138         throw CImgInstanceException(_cimg_instance
10139                                     "atXY() : Empty instance.",
10140                                     cimg_instance);
10141       return _atXY(x,y,z,c);
10142     }
10143 
10144     T& _atXY(const int x, const int y, const int z=0, const int c=0) {
10145       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
10146     }
10147 
10148     T _atXY(const int x, const int y, const int z=0, const int c=0) const {
10149       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
10150     }
10151 
10152     //! Read a pixel value with Dirichlet boundary conditions for the three first coordinates (\c x,\c y,\c z).
10153     T& atXYZ(const int x, const int y, const int z, const int c, const T out_val) {
10154       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
10155         (cimg::temporary(out_val)=out_val):(*this)(x,y,z,c);
10156     }
10157 
10158     T atXYZ(const int x, const int y, const int z, const int c, const T out_val) const {
10159       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_val:(*this)(x,y,z,c);
10160     }
10161 
10162     //! Read a pixel value with Neumann boundary conditions for the three first coordinates (\c x,\c y,\c z).
10163     T& atXYZ(const int x, const int y, const int z, const int c=0) {
10164       if (is_empty())
10165         throw CImgInstanceException(_cimg_instance
10166                                     "atXYZ() : Empty instance.",
10167                                     cimg_instance);
10168       return _atXYZ(x,y,z,c);
10169     }
10170 
10171     T atXYZ(const int x, const int y, const int z, const int c=0) const {
10172       if (is_empty())
10173         throw CImgInstanceException(_cimg_instance
10174                                     "atXYZ() : Empty instance.",
10175                                     cimg_instance);
10176       return _atXYZ(x,y,z,c);
10177     }
10178 
10179     T& _atXYZ(const int x, const int y, const int z, const int c=0) {
10180       return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
10181                      z<0?0:(z>=depth()?depth()-1:z),c);
10182     }
10183 
10184     T _atXYZ(const int x, const int y, const int z, const int c=0) const {
10185       return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
10186                      z<0?0:(z>=depth()?depth()-1:z),c);
10187     }
10188 
10189     //! Read a pixel value with Dirichlet boundary conditions.
10190     T& atXYZC(const int x, const int y, const int z, const int c, const T out_val) {
10191       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
10192         (cimg::temporary(out_val)=out_val):(*this)(x,y,z,c);
10193     }
10194 
10195     T atXYZC(const int x, const int y, const int z, const int c, const T out_val) const {
10196       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_val:(*this)(x,y,z,c);
10197     }
10198 
10199     //! Read a pixel value with Neumann boundary conditions.
10200     T& atXYZC(const int x, const int y, const int z, const int c) {
10201       if (is_empty())
10202         throw CImgInstanceException(_cimg_instance
10203                                     "atXYZC() : Empty instance.",
10204                                     cimg_instance);
10205       return _atXYZC(x,y,z,c);
10206     }
10207 
10208     T atXYZC(const int x, const int y, const int z, const int c) const {
10209       if (is_empty())
10210         throw CImgInstanceException(_cimg_instance
10211                                     "atXYZC() : Empty instance.",
10212                                     cimg_instance);
10213       return _atXYZC(x,y,z,c);
10214     }
10215 
10216     T& _atXYZC(const int x, const int y, const int z, const int c) {
10217       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
10218                      z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
10219     }
10220 
10221     T _atXYZC(const int x, const int y, const int z, const int c) const {
10222       return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
10223                      z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
10224     }
10225 
10226     //! Read a pixel value using linear interpolation and Dirichlet boundary conditions (first coordinate).
10227     Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T out_val) const {
10228       const int
10229         x = (int)fx - (fx>=0?0:1), nx = x + 1;
10230       const float
10231         dx = fx - x;
10232       const Tfloat
10233         Ic = (Tfloat)atX(x,y,z,c,out_val), In = (Tfloat)atXY(nx,y,z,c,out_val);
10234       return Ic + dx*(In-Ic);
10235     }
10236 
10237     //! Read a pixel value using linear interpolation and Neumann boundary conditions (first coordinate).
10238     Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
10239       if (is_empty())
10240         throw CImgInstanceException(_cimg_instance
10241                                     "linear_atX() : Empty instance.",
10242                                     cimg_instance);
10243 
10244       return _linear_atX(fx,y,z,c);
10245     }
10246 
10247     Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
10248       const float
10249         nfx = fx<0?0:(fx>_width-1?_width-1:fx);
10250       const unsigned int
10251         x = (unsigned int)nfx;
10252       const float
10253         dx = nfx - x;
10254       const unsigned int
10255         nx = dx>0?x+1:x;
10256       const Tfloat
10257         Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
10258       return Ic + dx*(In-Ic);
10259     }
10260 
10261     //! Read a pixel value using linear interpolation and Dirichlet boundary conditions (first two coordinates).
10262     Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T out_val) const {
10263       const int
10264         x = (int)fx - (fx>=0?0:1), nx = x + 1,
10265         y = (int)fy - (fy>=0?0:1), ny = y + 1;
10266       const float
10267         dx = fx - x,
10268         dy = fy - y;
10269       const Tfloat
10270         Icc = (Tfloat)atXY(x,y,z,c,out_val),  Inc = (Tfloat)atXY(nx,y,z,c,out_val),
10271         Icn = (Tfloat)atXY(x,ny,z,c,out_val), Inn = (Tfloat)atXY(nx,ny,z,c,out_val);
10272       return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
10273     }
10274 
10275     //! Read a pixel value using linear interpolation and Neumann boundary conditions (first two coordinates).
10276     Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
10277       if (is_empty())
10278         throw CImgInstanceException(_cimg_instance
10279                                     "linear_atXY() : Empty instance.",
10280                                     cimg_instance);
10281 
10282       return _linear_atXY(fx,fy,z,c);
10283     }
10284 
10285     Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
10286       const float
10287         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
10288         nfy = fy<0?0:(fy>_height-1?_height-1:fy);
10289       const unsigned int
10290         x = (unsigned int)nfx,
10291         y = (unsigned int)nfy;
10292       const float
10293         dx = nfx - x,
10294         dy = nfy - y;
10295       const unsigned int
10296         nx = dx>0?x+1:x,
10297         ny = dy>0?y+1:y;
10298       const Tfloat
10299         Icc = (Tfloat)(*this)(x,y,z,c),  Inc = (Tfloat)(*this)(nx,y,z,c),
10300         Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
10301       return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
10302     }
10303 
10304     //! Read a pixel value using linear interpolation and Dirichlet boundary conditions (first three coordinates).
10305     Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_val) const {
10306       const int
10307         x = (int)fx - (fx>=0?0:1), nx = x + 1,
10308         y = (int)fy - (fy>=0?0:1), ny = y + 1,
10309         z = (int)fz - (fz>=0?0:1), nz = z + 1;
10310       const float
10311         dx = fx - x,
10312         dy = fy - y,
10313         dz = fz - z;
10314       const Tfloat
10315         Iccc = (Tfloat)atXYZ(x,y,z,c,out_val), Incc = (Tfloat)atXYZ(nx,y,z,c,out_val),
10316         Icnc = (Tfloat)atXYZ(x,ny,z,c,out_val), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_val),
10317         Iccn = (Tfloat)atXYZ(x,y,nz,c,out_val), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_val),
10318         Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_val), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_val);
10319       return Iccc +
10320         dx*(Incc-Iccc +
10321             dy*(Iccc+Innc-Icnc-Incc +
10322                 dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
10323             dz*(Iccc+Incn-Iccn-Incc)) +
10324         dy*(Icnc-Iccc +
10325             dz*(Iccc+Icnn-Iccn-Icnc)) +
10326         dz*(Iccn-Iccc);
10327     }
10328 
10329     //! Read a pixel value using linear interpolation and Neumann boundary conditions (first three coordinates).
10330     Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
10331       if (is_empty())
10332         throw CImgInstanceException(_cimg_instance
10333                                     "linear_atXYZ() : Empty instance.",
10334                                     cimg_instance);
10335 
10336       return _linear_atXYZ(fx,fy,fz,c);
10337     }
10338 
10339     Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
10340       const float
10341         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
10342         nfy = fy<0?0:(fy>_height-1?_height-1:fy),
10343         nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
10344       const unsigned int
10345         x = (unsigned int)nfx,
10346         y = (unsigned int)nfy,
10347         z = (unsigned int)nfz;
10348       const float
10349         dx = nfx - x,
10350         dy = nfy - y,
10351         dz = nfz - z;
10352       const unsigned int
10353         nx = dx>0?x+1:x,
10354         ny = dy>0?y+1:y,
10355         nz = dz>0?z+1:z;
10356       const Tfloat
10357         Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
10358         Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
10359         Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
10360         Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
10361       return Iccc +
10362         dx*(Incc-Iccc +
10363             dy*(Iccc+Innc-Icnc-Incc +
10364                 dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
10365             dz*(Iccc+Incn-Iccn-Incc)) +
10366         dy*(Icnc-Iccc +
10367             dz*(Iccc+Icnn-Iccn-Icnc)) +
10368         dz*(Iccn-Iccc);
10369     }
10370 
10371     //! Read a pixel value using linear interpolation and Dirichlet boundary conditions.
10372     Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T out_val) const {
10373       const int
10374         x = (int)fx - (fx>=0?0:1), nx = x + 1,
10375         y = (int)fy - (fy>=0?0:1), ny = y + 1,
10376         z = (int)fz - (fz>=0?0:1), nz = z + 1,
10377         c = (int)fc - (fc>=0?0:1), nc = c + 1;
10378       const float
10379         dx = fx - x,
10380         dy = fy - y,
10381         dz = fz - z,
10382         dc = fc - c;
10383       const Tfloat
10384         Icccc = (Tfloat)atXYZC(x,y,z,c,out_val), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_val),
10385         Icncc = (Tfloat)atXYZC(x,ny,z,c,out_val), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_val),
10386         Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_val), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_val),
10387         Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_val), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_val),
10388         Icccn = (Tfloat)atXYZC(x,y,z,nc,out_val), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_val),
10389         Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_val), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_val),
10390         Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_val), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_val),
10391         Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_val), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_val);
10392       return Icccc +
10393         dx*(Inccc-Icccc +
10394             dy*(Icccc+Inncc-Icncc-Inccc +
10395                 dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
10396                     dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
10397                 dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
10398             dz*(Icccc+Incnc-Iccnc-Inccc +
10399                 dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
10400             dc*(Icccc+Inccn-Inccc-Icccn)) +
10401         dy*(Icncc-Icccc +
10402             dz*(Icccc+Icnnc-Iccnc-Icncc +
10403                 dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
10404             dc*(Icccc+Icncn-Icncc-Icccn)) +
10405         dz*(Iccnc-Icccc +
10406             dc*(Icccc+Iccnn-Iccnc-Icccn)) +
10407         dc*(Icccn-Icccc);
10408     }
10409 
10410     //! Read a pixel value using linear interpolation and Neumann boundary conditions.
10411     Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
10412       if (is_empty())
10413         throw CImgInstanceException(_cimg_instance
10414                                     "linear_atXYZC() : Empty instance.",
10415                                     cimg_instance);
10416 
10417       return _linear_atXYZC(fx,fy,fz,fc);
10418     }
10419 
10420     Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
10421       const float
10422         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
10423         nfy = fy<0?0:(fy>_height-1?_height-1:fy),
10424         nfz = fz<0?0:(fz>_depth-1?_depth-1:fz),
10425         nfc = fc<0?0:(fc>_spectrum-1?_spectrum-1:fc);
10426       const unsigned int
10427         x = (unsigned int)nfx,
10428         y = (unsigned int)nfy,
10429         z = (unsigned int)nfz,
10430         c = (unsigned int)nfc;
10431       const float
10432         dx = nfx - x,
10433         dy = nfy - y,
10434         dz = nfz - z,
10435         dc = nfc - c;
10436       const unsigned int
10437         nx = dx>0?x+1:x,
10438         ny = dy>0?y+1:y,
10439         nz = dz>0?z+1:z,
10440         nc = dc>0?c+1:c;
10441       const Tfloat
10442         Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
10443         Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
10444         Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
10445         Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
10446         Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
10447         Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
10448         Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
10449         Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
10450       return Icccc +
10451         dx*(Inccc-Icccc +
10452             dy*(Icccc+Inncc-Icncc-Inccc +
10453                 dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
10454                     dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
10455                 dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
10456             dz*(Icccc+Incnc-Iccnc-Inccc +
10457                 dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
10458             dc*(Icccc+Inccn-Inccc-Icccn)) +
10459         dy*(Icncc-Icccc +
10460             dz*(Icccc+Icnnc-Iccnc-Icncc +
10461                 dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
10462             dc*(Icccc+Icncn-Icncc-Icccn)) +
10463         dz*(Iccnc-Icccc +
10464             dc*(Icccc+Iccnn-Iccnc-Icccn)) +
10465         dc*(Icccn-Icccc);
10466     }
10467 
10468     //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions (first coordinates).
10469     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_val) const {
10470       const int
10471         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
10472       const float
10473         dx = fx - x;
10474       const Tfloat
10475         Ip = (Tfloat)atX(px,y,z,c,out_val), Ic = (Tfloat)atX(x,y,z,c,out_val),
10476         In = (Tfloat)atX(nx,y,z,c,out_val), Ia = (Tfloat)atX(ax,y,z,c,out_val);
10477       return Ic + 0.5f*(dx*(-Ip+In) + dx*dx*(2*Ip-5*Ic+4*In-Ia) + dx*dx*dx*(-Ip+3*Ic-3*In+Ia));
10478     }
10479 
10480     //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions (first coordinates).
10481     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_val,
10482                      const Tfloat min_val, const Tfloat max_val) const {
10483       const Tfloat val = cubic_atX(fx,y,z,c,out_val);
10484       return val<min_val?min_val:val>max_val?max_val:val;
10485     }
10486 
10487     //! Read a pixel value using cubic interpolation and Neumann boundary conditions (first coordinates).
10488     Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
10489       if (is_empty())
10490         throw CImgInstanceException(_cimg_instance
10491                                     "cubic_atX() : Empty instance.",
10492                                     cimg_instance);
10493       return _cubic_atX(fx,y,z,c);
10494     }
10495 
10496     //! Read a pixel value using cubic interpolation and Neumann boundary conditions (first coordinates).
10497     Tfloat cubic_atX(const float fx, const int y, const int z, const int c,
10498                      const Tfloat min_val, const Tfloat max_val) const {
10499       const Tfloat val = cubic_atX(fx,y,z,c);
10500       return val<min_val?min_val:val>max_val?max_val:val;
10501     }
10502 
10503     //! Read a pixel value using cubic interpolation and Neumann boundary conditions (first coordinates).
10504     Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
10505       const float
10506         nfx = fx<0?0:(fx>_width-1?_width-1:fx);
10507       const int
10508         x = (int)nfx;
10509       const float
10510         dx = nfx - x;
10511       const int
10512         px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2;
10513       const Tfloat
10514         Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
10515         In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
10516       return Ic + 0.5f*(dx*(-Ip+In) + dx*dx*(2*Ip-5*Ic+4*In-Ia) + dx*dx*dx*(-Ip+3*Ic-3*In+Ia));
10517     }
10518 
10519     //! Read a pixel value using cubic interpolation and Neumann boundary conditions (first coordinates).
10520     Tfloat _cubic_atX(const float fx, const int y, const int z, const int c,
10521                       const Tfloat min_val, const Tfloat max_val) const {
10522       const Tfloat val = _cubic_atX(fx,y,z,c);
10523       return val<min_val?min_val:val>max_val?max_val:val;
10524     }
10525 
10526     //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions.
10527     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_val) const {
10528       const int
10529         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
10530         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
10531       const float dx = fx - x, dy = fy - y;
10532       const Tfloat
10533         Ipp = (Tfloat)atXY(px,py,z,c,out_val), Icp = (Tfloat)atXY(x,py,z,c,out_val), Inp = (Tfloat)atXY(nx,py,z,c,out_val), Iap = (Tfloat)atXY(ax,py,z,c,out_val),
10534         Ip = Icp + 0.5f*(dx*(-Ipp+Inp) + dx*dx*(2*Ipp-5*Icp+4*Inp-Iap) + dx*dx*dx*(-Ipp+3*Icp-3*Inp+Iap)),
10535         Ipc = (Tfloat)atXY(px,y,z,c,out_val),  Icc = (Tfloat)atXY(x, y,z,c,out_val), Inc = (Tfloat)atXY(nx,y,z,c,out_val),  Iac = (Tfloat)atXY(ax,y,z,c,out_val),
10536         Ic = Icc + 0.5f*(dx*(-Ipc+Inc) + dx*dx*(2*Ipc-5*Icc+4*Inc-Iac) + dx*dx*dx*(-Ipc+3*Icc-3*Inc+Iac)),
10537         Ipn = (Tfloat)atXY(px,ny,z,c,out_val), Icn = (Tfloat)atXY(x,ny,z,c,out_val), Inn = (Tfloat)atXY(nx,ny,z,c,out_val), Ian = (Tfloat)atXY(ax,ny,z,c,out_val),
10538         In = Icn + 0.5f*(dx*(-Ipn+Inn) + dx*dx*(2*Ipn-5*Icn+4*Inn-Ian) + dx*dx*dx*(-Ipn+3*Icn-3*Inn+Ian)),
10539         Ipa = (Tfloat)atXY(px,ay,z,c,out_val), Ica = (Tfloat)atXY(x,ay,z,c,out_val), Ina = (Tfloat)atXY(nx,ay,z,c,out_val), Iaa = (Tfloat)atXY(ax,ay,z,c,out_val),
10540         Ia = Ica + 0.5f*(dx*(-Ipa+Ina) + dx*dx*(2*Ipa-5*Ica+4*Ina-Iaa) + dx*dx*dx*(-Ipa+3*Ica-3*Ina+Iaa));
10541       return Ic + 0.5f*(dy*(-Ip+In) + dy*dy*(2*Ip-5*Ic+4*In-Ia) + dy*dy*dy*(-Ip+3*Ic-3*In+Ia));
10542     }
10543 
10544     //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions.
10545     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_val,
10546                       const Tfloat min_val, const Tfloat max_val) const {
10547       const Tfloat val = cubic_atXY(fx,fy,z,c,out_val);
10548       return val<min_val?min_val:val>max_val?max_val:val;
10549     }
10550 
10551     //! Read a pixel value using cubic interpolation and Neumann boundary conditions.
10552     Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
10553       if (is_empty())
10554         throw CImgInstanceException(_cimg_instance
10555                                     "cubic_atXY() : Empty instance.",
10556                                     cimg_instance);
10557 
10558       return _cubic_atXY(fx,fy,z,c);
10559     }
10560 
10561     //! Read a pixel value using cubic interpolation and Neumann boundary conditions.
10562     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c,
10563                       const Tfloat min_val, const Tfloat max_val) const {
10564       const Tfloat val = cubic_atXY(fx,fy,z,c);
10565       return val<min_val?min_val:val>max_val?max_val:val;
10566     }
10567 
10568     //! Read a pixel value using cubic interpolation and Neumann boundary conditions.
10569     Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
10570       const float
10571         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
10572         nfy = fy<0?0:(fy>_height-1?_height-1:fy);
10573       const int x = (int)nfx, y = (int)nfy;
10574       const float dx = nfx - x, dy = nfy - y;
10575       const int
10576         px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
10577         py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2;
10578       const Tfloat
10579         Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), Iap = (Tfloat)(*this)(ax,py,z,c),
10580         Ip = Icp + 0.5f*(dx*(-Ipp+Inp) + dx*dx*(2*Ipp-5*Icp+4*Inp-Iap) + dx*dx*dx*(-Ipp+3*Icp-3*Inp+Iap)),
10581         Ipc = (Tfloat)(*this)(px,y,z,c),  Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),  Iac = (Tfloat)(*this)(ax,y,z,c),
10582         Ic = Icc + 0.5f*(dx*(-Ipc+Inc) + dx*dx*(2*Ipc-5*Icc+4*Inc-Iac) + dx*dx*dx*(-Ipc+3*Icc-3*Inc+Iac)),
10583         Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), Ian = (Tfloat)(*this)(ax,ny,z,c),
10584         In = Icn + 0.5f*(dx*(-Ipn+Inn) + dx*dx*(2*Ipn-5*Icn+4*Inn-Ian) + dx*dx*dx*(-Ipn+3*Icn-3*Inn+Ian)),
10585         Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), Iaa = (Tfloat)(*this)(ax,ay,z,c),
10586         Ia = Ica + 0.5f*(dx*(-Ipa+Ina) + dx*dx*(2*Ipa-5*Ica+4*Ina-Iaa) + dx*dx*dx*(-Ipa+3*Ica-3*Ina+Iaa));
10587       return Ic + 0.5f*(dy*(-Ip+In) + dy*dy*(2*Ip-5*Ic+4*In-Ia) + dy*dy*dy*(-Ip+3*Ic-3*In+Ia));
10588     }
10589 
10590     //! Read a pixel value using cubic interpolation and Neumann boundary conditions.
10591     Tfloat _cubic_atXY(const float fx, const float fy, const int z, const int c,
10592                        const Tfloat min_val, const Tfloat max_val) const {
10593       const Tfloat val = _cubic_atXY(fx,fy,z,c);
10594       return val<min_val?min_val:val>max_val?max_val:val;
10595     }
10596 
10597     //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions.
10598     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_val) const {
10599       const int
10600         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
10601         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
10602         z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
10603       const float dx = fx - x, dy = fy - y, dz = fz - z;
10604       const Tfloat
10605         Ippp = (Tfloat)atXYZ(px,py,pz,c,out_val), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_val),
10606         Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_val), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_val),
10607         Ipp = Icpp + 0.5f*(dx*(-Ippp+Inpp) + dx*dx*(2*Ippp-5*Icpp+4*Inpp-Iapp) + dx*dx*dx*(-Ippp+3*Icpp-3*Inpp+Iapp)),
10608         Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_val),  Iccp = (Tfloat)atXYZ(x, y,pz,c,out_val),
10609         Incp = (Tfloat)atXYZ(nx,y,pz,c,out_val),  Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_val),
10610         Icp = Iccp + 0.5f*(dx*(-Ipcp+Incp) + dx*dx*(2*Ipcp-5*Iccp+4*Incp-Iacp) + dx*dx*dx*(-Ipcp+3*Iccp-3*Incp+Iacp)),
10611         Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_val), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_val),
10612         Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_val), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_val),
10613         Inp = Icnp + 0.5f*(dx*(-Ipnp+Innp) + dx*dx*(2*Ipnp-5*Icnp+4*Innp-Ianp) + dx*dx*dx*(-Ipnp+3*Icnp-3*Innp+Ianp)),
10614         Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_val), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_val),
10615         Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_val), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_val),
10616         Iap = Icap + 0.5f*(dx*(-Ipap+Inap) + dx*dx*(2*Ipap-5*Icap+4*Inap-Iaap) + dx*dx*dx*(-Ipap+3*Icap-3*Inap+Iaap)),
10617         Ip = Icp + 0.5f*(dy*(-Ipp+Inp) + dy*dy*(2*Ipp-5*Icp+4*Inp-Iap) + dy*dy*dy*(-Ipp+3*Icp-3*Inp+Iap)),
10618         Ippc = (Tfloat)atXYZ(px,py,z,c,out_val), Icpc = (Tfloat)atXYZ(x,py,z,c,out_val),
10619         Inpc = (Tfloat)atXYZ(nx,py,z,c,out_val), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_val),
10620         Ipc = Icpc + 0.5f*(dx*(-Ippc+Inpc) + dx*dx*(2*Ippc-5*Icpc+4*Inpc-Iapc) + dx*dx*dx*(-Ippc+3*Icpc-3*Inpc+Iapc)),
10621         Ipcc = (Tfloat)atXYZ(px,y,z,c,out_val),  Iccc = (Tfloat)atXYZ(x, y,z,c,out_val),
10622         Incc = (Tfloat)atXYZ(nx,y,z,c,out_val),  Iacc = (Tfloat)atXYZ(ax,y,z,c,out_val),
10623         Icc = Iccc + 0.5f*(dx*(-Ipcc+Incc) + dx*dx*(2*Ipcc-5*Iccc+4*Incc-Iacc) + dx*dx*dx*(-Ipcc+3*Iccc-3*Incc+Iacc)),
10624         Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_val), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_val),
10625         Innc = (Tfloat)atXYZ(nx,ny,z,c,out_val), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_val),
10626         Inc = Icnc + 0.5f*(dx*(-Ipnc+Innc) + dx*dx*(2*Ipnc-5*Icnc+4*Innc-Ianc) + dx*dx*dx*(-Ipnc+3*Icnc-3*Innc+Ianc)),
10627         Ipac = (Tfloat)atXYZ(px,ay,z,c,out_val), Icac = (Tfloat)atXYZ(x,ay,z,c,out_val),
10628         Inac = (Tfloat)atXYZ(nx,ay,z,c,out_val), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_val),
10629         Iac = Icac + 0.5f*(dx*(-Ipac+Inac) + dx*dx*(2*Ipac-5*Icac+4*Inac-Iaac) + dx*dx*dx*(-Ipac+3*Icac-3*Inac+Iaac)),
10630         Ic = Icc + 0.5f*(dy*(-Ipc+Inc) + dy*dy*(2*Ipc-5*Icc+4*Inc-Iac) + dy*dy*dy*(-Ipc+3*Icc-3*Inc+Iac)),
10631         Ippn = (Tfloat)atXYZ(px,py,nz,c,out_val), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_val),
10632         Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_val), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_val),
10633         Ipn = Icpn + 0.5f*(dx*(-Ippn+Inpn) + dx*dx*(2*Ippn-5*Icpn+4*Inpn-Iapn) + dx*dx*dx*(-Ippn+3*Icpn-3*Inpn+Iapn)),
10634         Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_val),  Iccn = (Tfloat)atXYZ(x, y,nz,c,out_val),
10635         Incn = (Tfloat)atXYZ(nx,y,nz,c,out_val),  Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_val),
10636         Icn = Iccn + 0.5f*(dx*(-Ipcn+Incn) + dx*dx*(2*Ipcn-5*Iccn+4*Incn-Iacn) + dx*dx*dx*(-Ipcn+3*Iccn-3*Incn+Iacn)),
10637         Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_val), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_val),
10638         Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_val), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_val),
10639         Inn = Icnn + 0.5f*(dx*(-Ipnn+Innn) + dx*dx*(2*Ipnn-5*Icnn+4*Innn-Iann) + dx*dx*dx*(-Ipnn+3*Icnn-3*Innn+Iann)),
10640         Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_val), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_val),
10641         Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_val), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_val),
10642         Ian = Ican + 0.5f*(dx*(-Ipan+Inan) + dx*dx*(2*Ipan-5*Ican+4*Inan-Iaan) + dx*dx*dx*(-Ipan+3*Ican-3*Inan+Iaan)),
10643         In = Icn + 0.5f*(dy*(-Ipn+Inn) + dy*dy*(2*Ipn-5*Icn+4*Inn-Ian) + dy*dy*dy*(-Ipn+3*Icn-3*Inn+Ian)),
10644         Ippa = (Tfloat)atXYZ(px,py,az,c,out_val), Icpa = (Tfloat)atXYZ(x,py,az,c,out_val),
10645         Inpa = (Tfloat)atXYZ(nx,py,az,c,out_val), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_val),
10646         Ipa = Icpa + 0.5f*(dx*(-Ippa+Inpa) + dx*dx*(2*Ippa-5*Icpa+4*Inpa-Iapa) + dx*dx*dx*(-Ippa+3*Icpa-3*Inpa+Iapa)),
10647         Ipca = (Tfloat)atXYZ(px,y,az,c,out_val),  Icca = (Tfloat)atXYZ(x, y,az,c,out_val),
10648         Inca = (Tfloat)atXYZ(nx,y,az,c,out_val),  Iaca = (Tfloat)atXYZ(ax,y,az,c,out_val),
10649         Ica = Icca + 0.5f*(dx*(-Ipca+Inca) + dx*dx*(2*Ipca-5*Icca+4*Inca-Iaca) + dx*dx*dx*(-Ipca+3*Icca-3*Inca+Iaca)),
10650         Ipna = (Tfloat)atXYZ(px,ny,az,c,out_val), Icna = (Tfloat)atXYZ(x,ny,az,c,out_val),
10651         Inna = (Tfloat)atXYZ(nx,ny,az,c,out_val), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_val),
10652         Ina = Icna + 0.5f*(dx*(-Ipna+Inna) + dx*dx*(2*Ipna-5*Icna+4*Inna-Iana) + dx*dx*dx*(-Ipna+3*Icna-3*Inna+Iana)),
10653         Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_val), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_val),
10654         Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_val), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_val),
10655         Iaa = Icaa + 0.5f*(dx*(-Ipaa+Inaa) + dx*dx*(2*Ipaa-5*Icaa+4*Inaa-Iaaa) + dx*dx*dx*(-Ipaa+3*Icaa-3*Inaa+Iaaa)),
10656         Ia = Ica + 0.5f*(dy*(-Ipa+Ina) + dy*dy*(2*Ipa-5*Ica+4*Ina-Iaa) + dy*dy*dy*(-Ipa+3*Ica-3*Ina+Iaa));
10657       return Ic + 0.5f*(dz*(-Ip+In) + dz*dz*(2*Ip-5*Ic+4*In-Ia) + dz*dz*dz*(-Ip+3*Ic-3*In+Ia));
10658     }
10659 
10660     //! Read a pixel value using cubic interpolation and Dirichlet boundary conditions.
10661     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_val,
10662                        const Tfloat min_val, const Tfloat max_val) const {
10663       const Tfloat val = cubic_atXYZ(fx,fy,fz,c,out_val);
10664       return val<min_val?min_val:val>max_val?max_val:val;
10665     }
10666 
10667     //! Read a pixel value using cubic interpolation and Neumann boundary conditions.
10668     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
10669       if (is_empty())
10670         throw CImgInstanceException(_cimg_instance
10671                                     "cubic_atXYZ() : Empty instance.",
10672                                     cimg_instance);
10673 
10674       return _cubic_atXYZ(fx,fy,fz,c);
10675     }
10676 
10677     //! Read a pixel value using cubic interpolation and Neumann boundary conditions.
10678     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
10679                        const Tfloat min_val, const Tfloat max_val) const {
10680       const Tfloat val = cubic_atXYZ(fx,fy,fz,c);
10681       return val<min_val?min_val:val>max_val?max_val:val;
10682     }
10683 
10684     //! Read a pixel value using cubic interpolation and Neumann boundary conditions.
10685     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
10686       const float
10687         nfx = fx<0?0:(fx>_width-1?_width-1:fx),
10688         nfy = fy<0?0:(fy>_height-1?_height-1:fy),
10689         nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
10690       const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
10691       const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
10692       const int
10693         px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
10694         py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2,
10695         pz = z-1<0?0:z-1, nz = dz>0?z+1:z, az = z+2>=depth()?depth()-1:z+2;
10696       const Tfloat
10697         Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
10698         Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
10699         Ipp = Icpp + 0.5f*(dx*(-Ippp+Inpp) + dx*dx*(2*Ippp-5*Icpp+4*Inpp-Iapp) + dx*dx*dx*(-Ippp+3*Icpp-3*Inpp+Iapp)),
10700         Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
10701         Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
10702         Icp = Iccp + 0.5f*(dx*(-Ipcp+Incp) + dx*dx*(2*Ipcp-5*Iccp+4*Incp-Iacp) + dx*dx*dx*(-Ipcp+3*Iccp-3*Incp+Iacp)),
10703         Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
10704         Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
10705         Inp = Icnp + 0.5f*(dx*(-Ipnp+Innp) + dx*dx*(2*Ipnp-5*Icnp+4*Innp-Ianp) + dx*dx*dx*(-Ipnp+3*Icnp-3*Innp+Ianp)),
10706         Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
10707         Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
10708         Iap = Icap + 0.5f*(dx*(-Ipap+Inap) + dx*dx*(2*Ipap-5*Icap+4*Inap-Iaap) + dx*dx*dx*(-Ipap+3*Icap-3*Inap+Iaap)),
10709         Ip = Icp + 0.5f*(dy*(-Ipp+Inp) + dy*dy*(2*Ipp-5*Icp+4*Inp-Iap) + dy*dy*dy*(-Ipp+3*Icp-3*Inp+Iap)),
10710         Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
10711         Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
10712         Ipc = Icpc + 0.5f*(dx*(-Ippc+Inpc) + dx*dx*(2*Ippc-5*Icpc+4*Inpc-Iapc) + dx*dx*dx*(-Ippc+3*Icpc-3*Inpc+Iapc)),
10713         Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
10714         Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
10715         Icc = Iccc + 0.5f*(dx*(-Ipcc+Incc) + dx*dx*(2*Ipcc-5*Iccc+4*Incc-Iacc) + dx*dx*dx*(-Ipcc+3*Iccc-3*Incc+Iacc)),
10716         Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
10717         Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
10718         Inc = Icnc + 0.5f*(dx*(-Ipnc+Innc) + dx*dx*(2*Ipnc-5*Icnc+4*Innc-Ianc) + dx*dx*dx*(-Ipnc+3*Icnc-3*Innc+Ianc)),
10719         Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
10720         Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
10721         Iac = Icac + 0.5f*(dx*(-Ipac+Inac) + dx*dx*(2*Ipac-5*Icac+4*Inac-Iaac) + dx*dx*dx*(-Ipac+3*Icac-3*Inac+Iaac)),
10722         Ic = Icc + 0.5f*(dy*(-Ipc+Inc) + dy*dy*(2*Ipc-5*Icc+4*Inc-Iac) + dy*dy*dy*(-Ipc+3*Icc-3*Inc+Iac)),
10723         Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
10724         Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
10725         Ipn = Icpn + 0.5f*(dx*(-Ippn+Inpn) + dx*dx*(2*Ippn-5*Icpn+4*Inpn-Iapn) + dx*dx*dx*(-Ippn+3*Icpn-3*Inpn+Iapn)),
10726         Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
10727         Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
10728         Icn = Iccn + 0.5f*(dx*(-Ipcn+Incn) + dx*dx*(2*Ipcn-5*Iccn+4*Incn-Iacn) + dx*dx*dx*(-Ipcn+3*Iccn-3*Incn+Iacn)),
10729         Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
10730         Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
10731         Inn = Icnn + 0.5f*(dx*(-Ipnn+Innn) + dx*dx*(2*Ipnn-5*Icnn+4*Innn-Iann) + dx*dx*dx*(-Ipnn+3*Icnn-3*Innn+Iann)),
10732         Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
10733         Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
10734         Ian = Ican + 0.5f*(dx*(-Ipan+Inan) + dx*dx*(2*Ipan-5*Ican+4*Inan-Iaan) + dx*dx*dx*(-Ipan+3*Ican-3*Inan+Iaan)),
10735         In = Icn + 0.5f*(dy*(-Ipn+Inn) + dy*dy*(2*Ipn-5*Icn+4*Inn-Ian) + dy*dy*dy*(-Ipn+3*Icn-3*Inn+Ian)),
10736         Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
10737         Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
10738         Ipa = Icpa + 0.5f*(dx*(-Ippa+Inpa) + dx*dx*(2*Ippa-5*Icpa+4*Inpa-Iapa) + dx*dx*dx*(-Ippa+3*Icpa-3*Inpa+Iapa)),
10739         Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
10740         Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
10741         Ica = Icca + 0.5f*(dx*(-Ipca+Inca) + dx*dx*(2*Ipca-5*Icca+4*Inca-Iaca) + dx*dx*dx*(-Ipca+3*Icca-3*Inca+Iaca)),
10742         Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
10743         Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
10744         Ina = Icna + 0.5f*(dx*(-Ipna+Inna) + dx*dx*(2*Ipna-5*Icna+4*Inna-Iana) + dx*dx*dx*(-Ipna+3*Icna-3*Inna+Iana)),
10745         Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
10746         Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
10747         Iaa = Icaa + 0.5f*(dx*(-Ipaa+Inaa) + dx*dx*(2*Ipaa-5*Icaa+4*Inaa-Iaaa) + dx*dx*dx*(-Ipaa+3*Icaa-3*Inaa+Iaaa)),
10748         Ia = Ica + 0.5f*(dy*(-Ipa+Ina) + dy*dy*(2*Ipa-5*Ica+4*Ina-Iaa) + dy*dy*dy*(-Ipa+3*Ica-3*Ina+Iaa));
10749       return Ic + 0.5f*(dz*(-Ip+In) + dz*dz*(2*Ip-5*Ic+4*In-Ia) + dz*dz*dz*(-Ip+3*Ic-3*In+Ia));
10750     }
10751 
10752     //! Read a pixel value using cubic interpolation and Neumann boundary conditions.
10753     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
10754                         const Tfloat min_val, const Tfloat max_val) const {
10755       const Tfloat val = _cubic_atXYZ(fx,fy,fz,c);
10756       return val<min_val?min_val:val>max_val?max_val:val;
10757     }
10758 
10759     //! Set a pixel value, with 3d float coordinates, using linear interpolation.
10760     CImg<T>& set_linear_atXYZ(const T& val, const float fx, const float fy=0, const float fz=0, const int c=0,
10761                               const bool add=false) {
10762       const int
10763         x = (int)fx - (fx>=0?0:1), nx = x + 1,
10764         y = (int)fy - (fy>=0?0:1), ny = y + 1,
10765         z = (int)fz - (fz>=0?0:1), nz = z + 1;
10766       const float
10767         dx = fx - x,
10768         dy = fy - y,
10769         dz = fz - z;
10770       if (c>=0 && c<spectrum()) {
10771         if (z>=0 && z<depth()) {
10772           if (y>=0 && y<height()) {
10773             if (x>=0 && x<width()) {
10774               const float w1 = (1-dx)*(1-dy)*(1-dz), w2 = add?1:(1-w1);
10775               (*this)(x,y,z,c) = (T)(w1*val + w2*(*this)(x,y,z,c));
10776             }
10777             if (nx>=0 && nx<width()) {
10778               const float w1 = dx*(1-dy)*(1-dz), w2 = add?1:(1-w1);
10779               (*this)(nx,y,z,c) = (T)(w1*val + w2*(*this)(nx,y,z,c));
10780             }
10781           }
10782           if (ny>=0 && ny<height()) {
10783             if (x>=0 && x<width()) {
10784               const float w1 = (1-dx)*dy*(1-dz), w2 = add?1:(1-w1);
10785               (*this)(x,ny,z,c) = (T)(w1*val + w2*(*this)(x,ny,z,c));
10786             }
10787             if (nx>=0 && nx<width()) {
10788               const float w1 = dx*dy*(1-dz), w2 = add?1:(1-w1);
10789               (*this)(nx,ny,z,c) = (T)(w1*val + w2*(*this)(nx,ny,z,c));
10790             }
10791           }
10792         }
10793         if (nz>=0 && nz<depth()) {
10794           if (y>=0 && y<height()) {
10795             if (x>=0 && x<width()) {
10796               const float w1 = (1-dx)*(1-dy), w2 = add?1:(1-w1);
10797               (*this)(x,y,nz,c) = (T)(w1*val + w2*(*this)(x,y,nz,c));
10798             }
10799             if (nx>=0 && nx<width()) {
10800               const float w1 = dx*(1-dy), w2 = add?1:(1-w1);
10801               (*this)(nx,y,nz,c) = (T)(w1*val + w2*(*this)(nx,y,nz,c));
10802             }
10803           }
10804           if (ny>=0 && ny<height()) {
10805             if (x>=0 && x<width()) {
10806               const float w1 = (1-dx)*dy, w2 = add?1:(1-w1);
10807               (*this)(x,ny,nz,c) = (T)(w1*val + w2*(*this)(x,ny,nz,c));
10808             }
10809             if (nx>=0 && nx<width()) {
10810               const float w1 = dx*dy, w2 = add?1:(1-w1);
10811               (*this)(nx,ny,nz,c) = (T)(w1*val + w2*(*this)(nx,ny,nz,c));
10812             }
10813           }
10814         }
10815       }
10816       return *this;
10817     }
10818 
10819     //! Set a pixel value, with 2d float coordinates, using linear interpolation.
10820     CImg<T>& set_linear_atXY(const T& val, const float fx, const float fy=0, const int z=0, const int c=0,
10821                              const bool add=false) {
10822       const int
10823         x = (int)fx - (fx>=0?0:1), nx = x + 1,
10824         y = (int)fy - (fy>=0?0:1), ny = y + 1;
10825       const float
10826         dx = fx - x,
10827         dy = fy - y;
10828       if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
10829         if (y>=0 && y<height()) {
10830           if (x>=0 && x<width()) {
10831             const float w1 = (1-dx)*(1-dy), w2 = add?1:(1-w1);
10832             (*this)(x,y,z,c) = (T)(w1*val + w2*(*this)(x,y,z,c));
10833           }
10834           if (nx>=0 && nx<width()) {
10835             const float w1 = dx*(1-dy), w2 = add?1:(1-w1);
10836             (*this)(nx,y,z,c) = (T)(w1*val + w2*(*this)(nx,y,z,c));
10837           }
10838         }
10839         if (ny>=0 && ny<height()) {
10840           if (x>=0 && x<width()) {
10841             const float w1 = (1-dx)*dy, w2 = add?1:(1-w1);
10842             (*this)(x,ny,z,c) = (T)(w1*val + w2*(*this)(x,ny,z,c));
10843           }
10844           if (nx>=0 && nx<width()) {
10845             const float w1 = dx*dy, w2 = add?1:(1-w1);
10846             (*this)(nx,ny,z,c) = (T)(w1*val + w2*(*this)(nx,ny,z,c));
10847           }
10848         }
10849       }
10850       return *this;
10851     }
10852 
10853     //! Return a C-string containing the values of the instance image.
10854     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
10855       if (is_empty()) return CImg<charT>(1,1,1,1,0);
10856       const unsigned int siz = (unsigned int)size();
10857       CImgList<charT> items;
10858       char item[256] = { 0 };
10859       const T *ptrs = _data;
10860       for (unsigned int off = 0; off<siz-1; ++off) {
10861         std::sprintf(item,cimg::type<T>::format(),cimg::type<T>::format(*(ptrs++)));
10862         const unsigned int l = std::strlen(item);
10863         CImg<charT>(item,l+1).move_to(items).back()[l] = separator;
10864       }
10865       std::sprintf(item,cimg::type<T>::format(),cimg::type<T>::format(*ptrs));
10866       CImg<charT>(item,std::strlen(item)+1).move_to(items);
10867       CImg<ucharT> res; (items>'x').move_to(res);
10868       if (max_size) { res.crop(0,max_size); res(max_size) = 0; }
10869       return res;
10870     }
10871 
10872     //@}
10873     //-------------------------------------
10874     //
10875     //! \name Instance Checking
10876     //@{
10877     //-------------------------------------
10878 
10879     //! Return \c true if current image has shared memory.
10880     bool is_shared() const {
10881       return _is_shared;
10882     }
10883 
10884     //! Return \c true if current image is empty.
10885     bool is_empty() const {
10886       return !(_data && _width && _height && _depth && _spectrum);
10887     }
10888 
10889     //! Return \c true if image (*this) has the specified width.
10890     bool is_sameX(const unsigned int dx) const {
10891       return (_width==dx);
10892     }
10893 
10894     //! Return \c true if images \c (*this) and \c img have same width.
10895     template<typename t>
10896     bool is_sameX(const CImg<t>& img) const {
10897       return is_sameX(img._width);
10898     }
10899 
10900     //! Return \c true if images \c (*this) and the display \c disp have same width.
10901     bool is_sameX(const CImgDisplay& disp) const {
10902       return is_sameX((unsigned int)disp.width());
10903     }
10904 
10905     //! Return \c true if image (*this) has the specified height.
10906     bool is_sameY(const unsigned int dy) const {
10907       return (_height==dy);
10908     }
10909 
10910     //! Return \c true if images \c (*this) and \c img have same height.
10911     template<typename t>
10912     bool is_sameY(const CImg<t>& img) const {
10913       return is_sameY(img._height);
10914     }
10915 
10916     //! Return \c true if images \c (*this) and the display \c disp have same height.
10917     bool is_sameY(const CImgDisplay& disp) const {
10918       return is_sameY((unsigned int)disp.height());
10919     }
10920 
10921     //! Return \c true if image (*this) has the specified depth.
10922     bool is_sameZ(const unsigned int dz) const {
10923       return (_depth==dz);
10924     }
10925 
10926     //! Return \c true if images \c (*this) and \c img have same depth.
10927     template<typename t>
10928     bool is_sameZ(const CImg<t>& img) const {
10929       return is_sameZ(img._depth);
10930     }
10931 
10932     //! Return \c true if image (*this) has the specified number of channels.
10933     bool is_sameC(const unsigned int dc) const {
10934       return (_spectrum==dc);
10935     }
10936 
10937     //! Return \c true if images \c (*this) and \c img have same _spectrum.
10938     template<typename t>
10939     bool is_sameC(const CImg<t>& img) const {
10940       return is_sameC(img._spectrum);
10941     }
10942 
10943     //! Return \c true if image (*this) has the specified width and height.
10944     bool is_sameXY(const unsigned int dx, const unsigned int dy) const {
10945       return (is_sameX(dx) && is_sameY(dy));
10946     }
10947 
10948     //! Return \c true if images have same width and same height.
10949     template<typename t>
10950     bool is_sameXY(const CImg<t>& img) const {
10951       return (is_sameX(img) && is_sameY(img));
10952     }
10953 
10954     //! Return \c true if image \c (*this) and the display \c disp have same width and same height.
10955     bool is_sameXY(const CImgDisplay& disp) const {
10956       return (is_sameX(disp) && is_sameY(disp));
10957     }
10958 
10959     //! Return \c true if image (*this) has the specified width and depth.
10960     bool is_sameXZ(const unsigned int dx, const unsigned int dz) const {
10961       return (is_sameX(dx) && is_sameZ(dz));
10962     }
10963 
10964     //! Return \c true if images have same width and same depth.
10965     template<typename t>
10966     bool is_sameXZ(const CImg<t>& img) const {
10967       return (is_sameX(img) && is_sameZ(img));
10968     }
10969 
10970     //! Return \c true if image (*this) has the specified width and number of channels.
10971     bool is_sameXC(const unsigned int dx, const unsigned int dc) const {
10972       return (is_sameX(dx) && is_sameC(dc));
10973     }
10974 
10975     //! Return \c true if images have same width and same number of channels.
10976     template<typename t>
10977     bool is_sameXC(const CImg<t>& img) const {
10978       return (is_sameX(img) && is_sameC(img));
10979     }
10980 
10981     //! Return \c true if image (*this) has the specified height and depth.
10982     bool is_sameYZ(const unsigned int dy, const unsigned int dz) const {
10983       return (is_sameY(dy) && is_sameZ(dz));
10984     }
10985 
10986     //! Return \c true if images have same height and same depth.
10987     template<typename t>
10988     bool is_sameYZ(const CImg<t>& img) const {
10989       return (is_sameY(img) && is_sameZ(img));
10990     }
10991 
10992     //! Return \c true if image (*this) has the specified height and number of channels.
10993     bool is_sameYC(const unsigned int dy, const unsigned int dc) const {
10994       return (is_sameY(dy) && is_sameC(dc));
10995     }
10996 
10997     //! Return \c true if images have same height and same number of channels.
10998     template<typename t>
10999     bool is_sameYC(const CImg<t>& img) const {
11000       return (is_sameY(img) && is_sameC(img));
11001     }
11002 
11003     //! Return \c true if image (*this) has the specified depth and number of channels.
11004     bool is_sameZC(const unsigned int dz, const unsigned int dc) const {
11005       return (is_sameZ(dz) && is_sameC(dc));
11006     }
11007 
11008     //! Return \c true if images have same depth and same number of channels.
11009     template<typename t>
11010     bool is_sameZC(const CImg<t>& img) const {
11011       return (is_sameZ(img) && is_sameC(img));
11012     }
11013 
11014     //! Return \c true if image (*this) has the specified width, height and depth.
11015     bool is_sameXYZ(const unsigned int dx, const unsigned int dy, const unsigned int dz) const {
11016       return (is_sameXY(dx,dy) && is_sameZ(dz));
11017     }
11018 
11019     //! Return \c true if images have same width, same height and same depth.
11020     template<typename t>
11021     bool is_sameXYZ(const CImg<t>& img) const {
11022       return (is_sameXY(img) && is_sameZ(img));
11023     }
11024 
11025     //! Return \c true if image (*this) has the specified width, height and depth.
11026     bool is_sameXYC(const unsigned int dx, const unsigned int dy, const unsigned int dc) const {
11027       return (is_sameXY(dx,dy) && is_sameC(dc));
11028     }
11029 
11030     //! Return \c true if images have same width, same height and same number of channels.
11031     template<typename t>
11032     bool is_sameXYC(const CImg<t>& img) const {
11033       return (is_sameXY(img) && is_sameC(img));
11034     }
11035 
11036     //! Return \c true if image (*this) has the specified width, height and number of channels.
11037     bool is_sameXZC(const unsigned int dx, const unsigned int dz, const unsigned int dc) const {
11038       return (is_sameXZ(dx,dz) && is_sameC(dc));
11039     }
11040 
11041     //! Return \c true if images have same width, same depth and same number of channels.
11042     template<typename t>
11043     bool is_sameXZC(const CImg<t>& img) const {
11044       return (is_sameXZ(img) && is_sameC(img));
11045     }
11046 
11047     //! Return \c true if image (*this) has the specified height, depth and number of channels.
11048     bool is_sameYZC(const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
11049       return (is_sameYZ(dy,dz) && is_sameC(dc));
11050     }
11051 
11052     //! Return \c true if images have same heigth, same depth and same number of channels.
11053     template<typename t>
11054     bool is_sameYZC(const CImg<t>& img) const {
11055       return (is_sameYZ(img) && is_sameC(img));
11056     }
11057 
11058     //! Return \c true if image (*this) has the specified width, height, depth and number of channels.
11059     bool is_sameXYZC(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
11060       return (is_sameXYZ(dx,dy,dz) && is_sameC(dc));
11061     }
11062 
11063     //! Return \c true if images \c (*this) and \c img have same width, same height, same depth and same number of channels.
11064     template<typename t>
11065     bool is_sameXYZC(const CImg<t>& img) const {
11066       return (is_sameXYZ(img) && is_sameC(img));
11067     }
11068 
11069     //! Return \c true if pixel (x,y,z,c) is inside image boundaries.
11070     bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
11071       return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
11072     }
11073 
11074     //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x,y,z,c).
11075     template<typename t>
11076     bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
11077       const unsigned int wh = _width*_height, whd = wh*_depth, siz = whd*_spectrum;
11078       const T *const ppixel = &pixel;
11079       if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
11080       unsigned int off = (unsigned int)(ppixel - _data);
11081       const unsigned int nc = off/whd;
11082       off%=whd;
11083       const unsigned int nz = off/wh;
11084       off%=wh;
11085       const unsigned int ny = off/_width, nx = off%_width;
11086       x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
11087       return true;
11088     }
11089 
11090     //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x,y,z).
11091     template<typename t>
11092     bool contains(const T& pixel, t& x, t& y, t& z) const {
11093       const unsigned int wh = _width*_height, whd = wh*_depth, siz = whd*_spectrum;
11094       const T *const ppixel = &pixel;
11095       if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
11096       unsigned int off = ((unsigned int)(ppixel - _data))%whd;
11097       const unsigned int nz = off/wh;
11098       off%=wh;
11099       const unsigned int ny = off/_width, nx = off%_width;
11100       x = (t)nx; y = (t)ny; z = (t)nz;
11101       return true;
11102     }
11103 
11104     //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x,y).
11105     template<typename t>
11106     bool contains(const T& pixel, t& x, t& y) const {
11107       const unsigned int wh = _width*_height, siz = wh*_depth*_spectrum;
11108       const T *const ppixel = &pixel;
11109       if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
11110       unsigned int off = ((unsigned int)(ppixel - _data))%wh;
11111       const unsigned int ny = off/_width, nx = off%_width;
11112       x = (t)nx; y = (t)ny;
11113       return true;
11114     }
11115 
11116     //! Return \c true if specified referenced value is inside image boundaries. If true, returns pixel coordinates in (x).
11117     template<typename t>
11118     bool contains(const T& pixel, t& x) const {
11119       const T *const ppixel = &pixel;
11120       if (is_empty() || ppixel<_data || ppixel>=_data+size()) return false;
11121       x = (t)(((unsigned int)(ppixel - _data))%_width);
11122       return true;
11123     }
11124 
11125     //! Return \c true if specified referenced value is inside the image boundaries.
11126     bool contains(const T& pixel) const {
11127       const T *const ppixel = &pixel;
11128       return !is_empty() && ppixel>=_data && ppixel<_data + size();
11129     }
11130 
11131     //! Return \c true if the memory buffers of the two images overlaps.
11132     template<typename t>
11133     bool is_overlapped(const CImg<t>& img) const {
11134       const unsigned int csiz = size(), isiz = img.size();
11135       return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
11136     }
11137 
11138     //! Return true if the set (instance,primitives,colors,opacities) stands for a valid 3d object.
11139     template<typename tp, typename tc, typename to>
11140     bool is_object3d(const CImgList<tp>& primitives,
11141                      const CImgList<tc>& colors,
11142                      const to& opacities,
11143                      const bool full_check=true,
11144                      char *const error_message=0) const {
11145       if (error_message) *error_message = 0;
11146 
11147       // Check consistency for the particular case of an empty 3d object.
11148       if (is_empty()) {
11149         if (primitives || colors || opacities) {
11150           if (error_message) std::sprintf(error_message,
11151                                           "3d object has no vertices but %u primitives, %u colors and %u opacities",
11152                                           primitives._width,colors._width,opacities.size());
11153           return false;
11154         }
11155         return true;
11156       }
11157 
11158       // Check consistency of vertices.
11159       if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions.
11160         if (error_message) std::sprintf(error_message,
11161                                         "3d object (%u,%u) has invalid vertices dimensions (%u,%u,%u,%u)",
11162                                         _width,primitives._width,_width,_height,_depth,_spectrum);
11163         return false;
11164       }
11165       if (colors._width>primitives._width+1) {
11166         if (error_message) std::sprintf(error_message,
11167                                         "3d object (%u,%u) defines %u colors",
11168                                         _width,primitives._width,colors._width);
11169         return false;
11170       }
11171       if (opacities.size()>primitives._width) {
11172         if (error_message) std::sprintf(error_message,
11173                                         "3d object (%u,%u) defines %u opacities",
11174                                         _width,primitives._width,opacities.size());
11175         return false;
11176       }
11177       if (!full_check) return true;
11178 
11179       // Check consistency of primitives.
11180       cimglist_for(primitives,l) {
11181         const CImg<tp>& primitive = primitives[l];
11182         const unsigned int psiz = primitive.size();
11183         switch (psiz) {
11184         case 1 : { // Point.
11185           const unsigned int i0 = (unsigned int)primitive[0];
11186           if (i0>=_width) {
11187             if (error_message) std::sprintf(error_message,
11188                                             "3d object (%u,%u) refers to invalid vertex indice %u in point primitive %u",
11189                                             _width,primitives._width,i0,l);
11190             return false;
11191           }
11192         } break;
11193         case 5 : { // Sphere.
11194           const unsigned int
11195             i0 = (unsigned int)primitive(0),
11196             i1 = (unsigned int)primitive(1);
11197           if (i0>=_width || i1>=_width) {
11198             if (error_message) std::sprintf(error_message,
11199                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u",
11200                                             _width,primitives._width,i0,i1,l);
11201             return false;
11202           }
11203         } break;
11204         case 2 : // Segment.
11205         case 6 : {
11206           const unsigned int
11207             i0 = (unsigned int)primitive(0),
11208             i1 = (unsigned int)primitive(1);
11209           if (i0>=_width || i1>=_width) {
11210             if (error_message) std::sprintf(error_message,
11211                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u",
11212                                             _width,primitives._width,i0,i1,l);
11213             return false;
11214           }
11215         } break;
11216         case 3 : // Triangle.
11217         case 9 : {
11218           const unsigned int
11219             i0 = (unsigned int)primitive(0),
11220             i1 = (unsigned int)primitive(1),
11221             i2 = (unsigned int)primitive(2);
11222           if (i0>=_width || i1>=_width || i2>=_width) {
11223             if (error_message) std::sprintf(error_message,
11224                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u",
11225                                             _width,primitives._width,i0,i1,i2,l);
11226             return false;
11227           }
11228         } break;
11229         case 4 : // Quadrangle.
11230         case 12 : {
11231           const unsigned int
11232             i0 = (unsigned int)primitive(0),
11233             i1 = (unsigned int)primitive(1),
11234             i2 = (unsigned int)primitive(2),
11235             i3 = (unsigned int)primitive(3);
11236           if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
11237             if (error_message) std::sprintf(error_message,
11238                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u",
11239                                             _width,primitives._width,i0,i1,i2,i3,l);
11240             return false;
11241           }
11242         } break;
11243         default :
11244           if (error_message) std::sprintf(error_message,
11245                                           "3d object has invalid primitive %u of size %u",
11246                                           l,psiz);
11247           return false;
11248         }
11249       }
11250 
11251       // Check consistency of colors.
11252       cimglist_for(colors,c) {
11253         const CImg<tc>& color = colors[c];
11254         if (!color) {
11255           if (error_message) std::sprintf(error_message,
11256                                           "3d object has empty color for primitive %u",
11257                                           c);
11258           return false;
11259         }
11260       }
11261 
11262       // Check consistency of light texture.
11263       if (colors._width>primitives._width) {
11264         const CImg<tc> &light = colors.back();
11265         if (!light || light._depth>1) {
11266           if (error_message) std::sprintf(error_message,
11267                                           "3d object has invalid light texture (%u,%u,%u,%u)",
11268                                           light._width,light._height,light._depth,light._spectrum);
11269           return false;
11270         }
11271       }
11272 
11273       // Check consistency of opacities.
11274       return _is_object3d(opacities,error_message);
11275     }
11276 
11277     template<typename to>
11278     bool _is_object3d(const CImgList<to>& opacities, char *const error_message) const {
11279       cimglist_for(opacities,o) {
11280         const CImg<to>& opacity = opacities[o];
11281         if (opacity._spectrum!=1) {
11282           if (error_message) std::sprintf(error_message,
11283                                           "3d object has invalid opacity (%u,%u,%u,%u) for primitive %u",
11284                                           opacity._width,opacity._height,opacity._depth,opacity._spectrum,o);
11285           return false;
11286         }
11287       }
11288       return true;
11289     }
11290 
11291     template<typename to>
11292     bool _is_object3d(const CImg<to>&, char *const) const {
11293       return true;
11294     }
11295 
11296     //! Test if an image is a valid CImg3d object.
11297     bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
11298       if (error_message) *error_message = 0;
11299 
11300       // Check instance dimension and header.
11301       if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
11302         if (error_message) std::sprintf(error_message,
11303                                         "CImg3d has invalid dimensions (%u,%u,%u,%u)",
11304                                         _width,_height,_depth,_spectrum);
11305         return false;
11306       }
11307       const T *ptrs = _data, *const ptre = end();
11308       if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
11309           !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
11310         if (error_message) std::sprintf(error_message,
11311                                         "CImg3d header not found");
11312         return false;
11313       }
11314       const unsigned int nb_points = (unsigned int)*(ptrs++), nb_primitives = (unsigned int)*(ptrs++);
11315 
11316       // Check consistency of vertex data.
11317       if (!nb_points) {
11318         if (nb_primitives) {
11319           if (error_message) std::sprintf(error_message,
11320                                           "CImg3d has no vertices but %u primitives",
11321                                           nb_primitives);
11322           return false;
11323         }
11324         if (ptrs!=ptre) {
11325           if (error_message) std::sprintf(error_message,
11326                                           "CImg3d (%u,%u) is empty but contains %u byte%s more than expected",
11327                                           nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
11328           return false;
11329         }
11330         return true;
11331       }
11332       ptrs+=3*nb_points;
11333       if (ptrs>ptre) {
11334         if (error_message) std::sprintf(error_message,
11335                                         "CImg3d (%u,%u) has incomplete vertex data",
11336                                         nb_points,nb_primitives);
11337         return false;
11338       }
11339       if (!full_check) return true;
11340 
11341       // Check primitive consistency.
11342       if (ptrs==ptre) {
11343         if (error_message) std::sprintf(error_message,
11344                                         "CImg3d (%u,%u) has no primitive data",
11345                                         nb_points,nb_primitives);
11346         return false;
11347       }
11348       for (unsigned int p = 0; p<nb_primitives; ++p) {
11349         const unsigned int nb_inds = (unsigned int)*(ptrs++);
11350         switch (nb_inds) {
11351         case 1 : { // Point.
11352           const unsigned int ind0 = (unsigned int)*(ptrs++);
11353           if (ind0>=nb_points) {
11354             if (error_message) std::sprintf(error_message,
11355                                             "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive %u",
11356                                             nb_points,nb_primitives,ind0,p);
11357             return false;
11358           }
11359         } break;
11360         case 5 : { // Sphere.
11361           const unsigned int
11362             i0 = (unsigned int)*(ptrs++),
11363             i1 = (unsigned int)*(ptrs++);
11364           ptrs+=3;
11365           if (i0>=nb_points || i1>=nb_points) {
11366             if (error_message) std::sprintf(error_message,
11367                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u",
11368                                             nb_points,nb_primitives,i0,i1,p);
11369             return false;
11370           }
11371         } break;
11372         case 2 : case 6 : { // Segment.
11373           const unsigned int
11374             i0 = (unsigned int)*(ptrs++),
11375             i1 = (unsigned int)*(ptrs++);
11376           if (nb_inds==6) ptrs+=4;
11377           if (i0>=nb_points || i1>=nb_points) {
11378             if (error_message) std::sprintf(error_message,
11379                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u",
11380                                             nb_points,nb_primitives,i0,i1,p);
11381             return false;
11382           }
11383         } break;
11384         case 3 : case 9 : { // Triangle.
11385           const unsigned int
11386             i0 = (unsigned int)*(ptrs++),
11387             i1 = (unsigned int)*(ptrs++),
11388             i2 = (unsigned int)*(ptrs++);
11389           if (nb_inds==9) ptrs+=6;
11390           if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
11391             if (error_message) std::sprintf(error_message,
11392                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u",
11393                                             nb_points,nb_primitives,i0,i1,i2,p);
11394             return false;
11395           }
11396         } break;
11397         case 4 : case 12 : { // Quadrangle.
11398           const unsigned int
11399             i0 = (unsigned int)*(ptrs++),
11400             i1 = (unsigned int)*(ptrs++),
11401             i2 = (unsigned int)*(ptrs++),
11402             i3 = (unsigned int)*(ptrs++);
11403           if (nb_inds==12) ptrs+=8;
11404           if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
11405             if (error_message) std::sprintf(error_message,
11406                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u",
11407                                             nb_points,nb_primitives,i0,i1,i2,i3,p);
11408             return false;
11409           }
11410         } break;
11411         default :
11412           if (error_message) std::sprintf(error_message,
11413                                           "CImg3d (%u,%u) has invalid primitive %u of size %u",
11414                                           nb_points,nb_primitives,p,nb_inds);
11415           return false;
11416         }
11417         if (ptrs>ptre) {
11418           if (error_message) std::sprintf(error_message,
11419                                           "CImg3d (%u,%u) has incomplete primitive data for primitive %u",
11420                                           nb_points,nb_primitives,p);
11421           return false;
11422         }
11423       }
11424 
11425       // Check color consistency.
11426       if (ptrs==ptre) {
11427         if (error_message) std::sprintf(error_message,
11428                                         "CImg3d (%u,%u) has no color/texture data",
11429                                         nb_points,nb_primitives);
11430         return false;
11431       }
11432       for (unsigned int c = 0; c<nb_primitives; ++c) {
11433         if ((int)*(ptrs++)!=-128) ptrs+=2;
11434         else if ((ptrs+=3)<ptre) {
11435           const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
11436           if (!h && !s) {
11437             if (w>=c) {
11438               if (error_message) std::sprintf(error_message,
11439                                               "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u for primitive %u",
11440                                               nb_points,nb_primitives,w,c);
11441               return false;
11442             }
11443           } else ptrs+=w*h*s;
11444         }
11445         if (ptrs>ptre) {
11446           if (error_message) std::sprintf(error_message,
11447                                           "CImg3d (%u,%u) has incomplete color/texture data for primitive %u",
11448                                           nb_points,nb_primitives,c);
11449           return false;
11450         }
11451       }
11452 
11453       // Check opacity consistency.
11454       if (ptrs==ptre) {
11455         if (error_message) std::sprintf(error_message,
11456                                         "CImg3d (%u,%u) has no opacity data",
11457                                         nb_points,nb_primitives);
11458         return false;
11459       }
11460       for (unsigned int o = 0; o<nb_primitives; ++o) {
11461         if ((int)*(ptrs++)==-128 && (ptrs+=3)<ptre) {
11462           const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
11463           if (!h && !s) {
11464             if (w>=o) {
11465               if (error_message) std::sprintf(error_message,
11466                                               "CImg3d (%u,%u) refers to invalid shared opacity indice %u for primitive %u",
11467                                               nb_points,nb_primitives,w,o);
11468               return false;
11469             }
11470           } else ptrs+=w*h*s;
11471         }
11472         if (ptrs>ptre) {
11473           if (error_message) std::sprintf(error_message,
11474                                           "CImg3d (%u,%u) has incomplete opacity data for primitive %u",
11475                                           nb_points,nb_primitives,o);
11476           return false;
11477         }
11478       }
11479 
11480       // Check end of data.
11481       if (ptrs<ptre) {
11482         if (error_message) std::sprintf(error_message,
11483                                         "CImg3d (%u,%u) contains %u byte%s more than expected",
11484                                         nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
11485         return false;
11486       }
11487       return true;
11488     }
11489 
11490     static bool _is_CImg3d(const T val, const char c) {
11491       if (val>=(T)c && val<(T)(c+1)) return true;
11492       return false;
11493     }
11494 
11495     //@}
11496     //-------------------------------------
11497     //
11498     //! \name Mathematical Functions
11499     //@{
11500     //-------------------------------------
11501 
11502     // Define the math formula parser/compiler and evaluator.
11503     struct _cimg_math_parser {
11504       CImgList<charT> label;
11505       CImgList<uintT> code;
11506       CImg<uintT> level, opcode;
11507       CImg<doubleT> mem;
11508       CImg<charT> expr;
11509       const CImg<T>& reference;
11510       CImg<Tdouble> reference_stats;
11511       unsigned int mempos, result;
11512       const char *const calling_function;
11513 #define _cimg_mp_return(x) { *se = saved_char; return x; }
11514 #define _cimg_mp_opcode0(op) _cimg_mp_return(opcode0(op));
11515 #define _cimg_mp_opcode1(op,i1) _cimg_mp_return(opcode1(op,i1));
11516 #define _cimg_mp_opcode2(op,i1,i2) { const unsigned int _i1 = i1, _i2 = i2; _cimg_mp_return(opcode2(op,_i1,_i2)); }
11517 #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)); }
11518 #define _cimg_mp_opcode5(op,i1,i2,i3,i4,i5) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5; \
11519           _cimg_mp_return(opcode5(op,_i1,_i2,_i3,_i4,_i5)); }
11520 
11521       // Constructor - Destructor.
11522       _cimg_math_parser(const CImg<T>& img, const char *const expression, const char *const funcname=0):
11523         reference(img),calling_function(funcname?funcname:"cimg_math_parser") {
11524         unsigned int l = 0;
11525         if (expression) {
11526           l = std::strlen(expression);
11527           expr.assign(expression,l+1);
11528           if (*expr._data) {
11529             char *d = expr._data;
11530             for (const char *s = expr._data; *s || (bool)(*d=0); ++s) if (*s!=' ') *(d++) = *s;
11531             l = d - expr._data;
11532           }
11533         }
11534         if (!l) throw CImgArgumentException("[_cimg_math_parser] "
11535                                             "CImg<%s>::%s() : Empty specified expression.",
11536                                             pixel_type(),calling_function);
11537 
11538         int lv = 0; // Count parenthesis level of expression.
11539         level.assign(l);
11540         unsigned int *pd = level._data;
11541         for (const char *ps = expr._data; *ps && lv>=0; ++ps) *(pd++) = (unsigned int)(*ps=='('?lv++:*ps==')'?--lv:lv);
11542         if (lv!=0) {
11543           throw CImgArgumentException("[_cimg_math_parser] "
11544                                       "CImg<%s>::%s() : Unbalanced parentheses in specified expression '%s'.",
11545                                       pixel_type(),calling_function,
11546                                       expr._data);
11547         }
11548         mem.assign(512); label.assign(512); // Init constant values.
11549         mem[0] = 0; mem[1] = 1;
11550         mem[2] = (double)reference._width; mem[3] = (double)reference._height; mem[4] = (double)reference._depth; mem[5] = (double)reference._spectrum;
11551         mem[6] = cimg::PI; mem[7] = std::exp(1.0); // Then [8] = x, [9] = y, [10] = z, [11] = c
11552         mempos = 12;
11553         result = compile(expr._data,expr._data+l); // Compile formula into a serie of opcodes.
11554       }
11555 
11556       // Insert code instructions.
11557       unsigned int opcode0(const char op) {
11558         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
11559         const unsigned int pos = mempos++;
11560         CImg<uintT>::vector(op,pos).move_to(code);
11561         return pos;
11562       }
11563 
11564       unsigned int opcode1(const char op, const unsigned int arg1) {
11565         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
11566         const unsigned int pos = mempos++;
11567         CImg<uintT>::vector(op,pos,arg1).move_to(code);
11568         return pos;
11569       }
11570 
11571       unsigned int opcode2(const char op, const unsigned int arg1, const unsigned int arg2) {
11572         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
11573         const unsigned int pos = mempos++;
11574         CImg<uintT>::vector(op,pos,arg1,arg2).move_to(code);
11575         return pos;
11576       }
11577 
11578       unsigned int opcode3(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
11579         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
11580         const unsigned int pos = mempos++;
11581         CImg<uintT>::vector(op,pos,arg1,arg2,arg3).move_to(code);
11582         return pos;
11583       }
11584 
11585       unsigned int opcode5(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
11586                            const unsigned int arg4, const unsigned int arg5) {
11587         if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
11588         const unsigned int pos = mempos++;
11589         CImg<uintT>::vector(op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
11590         return pos;
11591       }
11592 
11593       // Compilation procedure.
11594       unsigned int compile(char *const ss, char *const se) {
11595         if (!ss || se<=ss || !*ss) {
11596           throw CImgArgumentException("[_cimg_math_parser] "
11597                                       "CImg<%s>::%s() : Missing item in specified expression '%s'.",
11598                                       pixel_type(),calling_function,
11599                                       expr._data);
11600         }
11601         char
11602           *const se1 = se-1, *const se2 = se-2, *const se3 = se-3, *const se4 = se-4,
11603           *const ss1 = ss+1, *const ss2 = ss+2, *const ss3 = ss+3, *const ss4 = ss+4,
11604           *const ss5 = ss+5, *const ss6 = ss+6, *const ss7 = ss+7;
11605         const char saved_char = *se; *se = 0;
11606         const unsigned int clevel = level[ss-expr._data], clevel1 = clevel+1;
11607 
11608         // Look for a single value, variable or variable assignment.
11609         char end = 0; double val = 0;
11610         if (std::sscanf(ss,"%lf%c",&val,&end)==1) {
11611           if (val==0) _cimg_mp_return(0);
11612           if (val==1) _cimg_mp_return(1);
11613           if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
11614           const unsigned int pos = mempos++;
11615           mem[pos] = val;
11616           _cimg_mp_return(pos);
11617         }
11618         if (ss1==se) switch (*ss) {
11619           case 'w' : _cimg_mp_return(2); case 'h' : _cimg_mp_return(3); case 'd' : _cimg_mp_return(4); case 's' : _cimg_mp_return(5);
11620           case 'x' : _cimg_mp_return(8); case 'y' : _cimg_mp_return(9); case 'z' : _cimg_mp_return(10); case 'c' : _cimg_mp_return(11);
11621           case 'e' : _cimg_mp_return(7);
11622           case 'u' : case '?' : _cimg_mp_opcode2(0,0,1);
11623           case 'g' : _cimg_mp_opcode0(1);
11624           case 'i' : _cimg_mp_opcode0(2);
11625           }
11626         if (ss1==se1) {
11627           if (*ss=='p' && *ss1=='i') _cimg_mp_return(6); // pi
11628           if (*ss=='i') {
11629             if (*ss1=='m') _cimg_mp_opcode0(57); // im
11630             if (*ss1=='M') _cimg_mp_opcode0(58); // iM
11631             if (*ss1=='a') _cimg_mp_opcode0(59); // ia
11632             if (*ss1=='v') _cimg_mp_opcode0(60); // iv
11633           }
11634           if (*ss1=='m') {
11635             if (*ss=='x') _cimg_mp_opcode0(61); // xm
11636             if (*ss=='y') _cimg_mp_opcode0(62); // ym
11637             if (*ss=='z') _cimg_mp_opcode0(63); // zm
11638             if (*ss=='c') _cimg_mp_opcode0(64); // cm
11639           }
11640           if (*ss1=='M') {
11641             if (*ss=='x') _cimg_mp_opcode0(65); // xM
11642             if (*ss=='y') _cimg_mp_opcode0(66); // yM
11643             if (*ss=='z') _cimg_mp_opcode0(67); // zM
11644             if (*ss=='c') _cimg_mp_opcode0(68); // cM
11645           }
11646         }
11647         if (ss3==se) {
11648           if (*ss=='x' && *ss1=='/' && *ss2=='w') _cimg_mp_opcode0(3);
11649           if (*ss=='y' && *ss1=='/' && *ss2=='h') _cimg_mp_opcode0(4);
11650           if (*ss=='z' && *ss1=='/' && *ss2=='d') _cimg_mp_opcode0(5);
11651           if (*ss=='c' && *ss1=='/' && *ss2=='s') _cimg_mp_opcode0(6);
11652         }
11653 
11654         // Look for variable declarations.
11655         for (char *s = se2; s>ss; --s) if (*s==';' && level[s-expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s+1,se)); }
11656         for (char *s = ss1, *ps = ss, *ns = ss2; s<se1; ++s, ++ps, ++ns)
11657            if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' && level[s-expr._data]==clevel) {
11658              CImg<charT> variable_name(ss,s-ss+1); variable_name.back() = 0;
11659              bool is_valid_name = true;
11660              if ((*ss>='0' && *ss<='9') ||
11661                  (s==ss+1 && (*ss=='x' || *ss=='y' || *ss=='z' || *ss=='c' ||
11662                               *ss=='w' || *ss=='h' || *ss=='d' || *ss=='s' ||
11663                               *ss=='e' || *ss=='u' || *ss=='g' || *ss=='i')) ||
11664                  (s==ss+2 && ((*ss=='p' && *(ss+1)=='i') ||
11665                               (*ss=='i' && (*(ss+1)=='m' || *(ss+1)=='M' || *(ss+1)=='a' || *(ss+1)=='v'))))) is_valid_name = false;
11666              for (const char *ns = ss; ns<s; ++ns)
11667                if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') {
11668                  is_valid_name = false; break;
11669                }
11670              if (!is_valid_name) {
11671                *se = saved_char;
11672                if (!std::strcmp(variable_name,"x") || !std::strcmp(variable_name,"y") || !std::strcmp(variable_name,"z") ||
11673                    !std::strcmp(variable_name,"c") || !std::strcmp(variable_name,"w") || !std::strcmp(variable_name,"h") ||
11674                    !std::strcmp(variable_name,"d") || !std::strcmp(variable_name,"s") || !std::strcmp(variable_name,"e") ||
11675                    !std::strcmp(variable_name,"u") || !std::strcmp(variable_name,"g") || !std::strcmp(variable_name,"i") ||
11676                    !std::strcmp(variable_name,"pi") || !std::strcmp(variable_name,"im") || !std::strcmp(variable_name,"iM") ||
11677                    !std::strcmp(variable_name,"ia") || !std::strcmp(variable_name,"iv"))
11678                  throw CImgArgumentException("[_cimg_math_parser] "
11679                                              "CImg<%s>::%s() : Invalid assignment of reserved variable name '%s' in specified expression '%s'.",
11680                                              pixel_type(),calling_function,
11681                                              variable_name._data,expr._data);
11682                else
11683                  throw CImgArgumentException("[_cimg_math_parser] "
11684                                              "CImg<%s>::%s() : Invalid variable name '%s' in specified expression '%s'.",
11685                                              pixel_type(),calling_function,
11686                                              variable_name._data,expr._data);
11687              }
11688              for (unsigned int i = 0; i<mempos; ++i) // Check for existing variable with same name.
11689                if (label[i]._data && !std::strcmp(variable_name,label[i])) {
11690                  *se = saved_char;
11691                  throw CImgArgumentException("[_cimg_math_parser] "
11692                                              "CImg<%s>::%s() : Invalid multiple assignments of variable '%s' in specified expression '%s'.",
11693                                              pixel_type(),calling_function,
11694                                              variable_name._data,expr._data);
11695                }
11696              const unsigned int src_pos = compile(s+1,se);
11697              if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
11698              const unsigned int dest_pos = mempos++;
11699              variable_name.move_to(label[dest_pos]);
11700              CImg<uintT>::vector(7,dest_pos,src_pos).move_to(code);
11701              _cimg_mp_return(dest_pos);
11702            }
11703 
11704         // Look for unary/binary operators. The operator precedences is defined as in C++.
11705         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='|' && *ns=='|' && level[s-expr._data]==clevel) _cimg_mp_opcode2(8,compile(ss,s),compile(s+2,se));
11706         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s-expr._data]==clevel) _cimg_mp_opcode2(9,compile(ss,s),compile(s+2,se));
11707         for (char *s = se2; s>ss; --s) if (*s=='|' && level[s-expr._data]==clevel) _cimg_mp_opcode2(10,compile(ss,s),compile(s+1,se));
11708         for (char *s = se2; s>ss; --s) if (*s=='&' && level[s-expr._data]==clevel) _cimg_mp_opcode2(11,compile(ss,s),compile(s+1,se));
11709         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='!' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(12,compile(ss,s),compile(s+2,se));
11710         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='=' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(13,compile(ss,s),compile(s+2,se));
11711         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(14,compile(ss,s),compile(s+2,se));
11712         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(15,compile(ss,s),compile(s+2,se));
11713         for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
11714           if (*s=='<' && *ns!='<' && *ps!='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(16,compile(ss,s),compile(s+1,se));
11715         for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
11716           if (*s=='>' && *ns!='>' && *ps!='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(17,compile(ss,s),compile(s+1,se));
11717         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(18,compile(ss,s),compile(s+2,se));
11718         for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(19,compile(ss,s),compile(s+2,se));
11719         for (char *s = se2, *ps = se3; s>ss; --s, --ps)
11720           if (*s=='-' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
11721               (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
11722             _cimg_mp_opcode2(20,compile(ss,s),compile(s+1,se));
11723         for (char *s = se2, *ps = se3; s>ss; --s, --ps)
11724           if (*s=='+' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
11725               (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
11726             _cimg_mp_opcode2(21,compile(ss,s),compile(s+1,se));
11727         for (char *s = se2; s>ss; --s) if (*s=='*' && level[s-expr._data]==clevel) _cimg_mp_opcode2(22,compile(ss,s),compile(s+1,se));
11728         for (char *s = se2; s>ss; --s) if (*s=='/' && level[s-expr._data]==clevel) _cimg_mp_opcode2(23,compile(ss,s),compile(s+1,se));
11729         for (char *s = se2; s>ss; --s) if (*s=='%' && level[s-expr._data]==clevel) _cimg_mp_opcode2(24,compile(ss,s),compile(s+1,se));
11730         if (ss<se1) {
11731           if (*ss=='-') _cimg_mp_opcode1(26,compile(ss1,se));
11732           if (*ss=='+') _cimg_mp_return(compile(ss1,se));
11733           if (*ss=='!') _cimg_mp_opcode1(27,compile(ss1,se));
11734           if (*ss=='~') _cimg_mp_opcode1(28,compile(ss1,se));
11735         }
11736         for (char *s = se2; s>ss; --s) if (*s=='^' && level[s-expr._data]==clevel) _cimg_mp_opcode2(25,compile(ss,s),compile(s+1,se));
11737 
11738         // Look for a function call or a parenthesis.
11739         if (*se1==')') {
11740           if (*ss=='(') _cimg_mp_return(compile(ss1,se1));
11741           if (!std::strncmp(ss,"sin(",4)) _cimg_mp_opcode1(29,compile(ss4,se1));
11742           if (!std::strncmp(ss,"cos(",4)) _cimg_mp_opcode1(30,compile(ss4,se1));
11743           if (!std::strncmp(ss,"tan(",4)) _cimg_mp_opcode1(31,compile(ss4,se1));
11744           if (!std::strncmp(ss,"asin(",5)) _cimg_mp_opcode1(32,compile(ss5,se1));
11745           if (!std::strncmp(ss,"acos(",5)) _cimg_mp_opcode1(33,compile(ss5,se1));
11746           if (!std::strncmp(ss,"atan(",5)) _cimg_mp_opcode1(34,compile(ss5,se1));
11747           if (!std::strncmp(ss,"sinh(",5)) _cimg_mp_opcode1(35,compile(ss5,se1));
11748           if (!std::strncmp(ss,"cosh(",5)) _cimg_mp_opcode1(36,compile(ss5,se1));
11749           if (!std::strncmp(ss,"tanh(",5)) _cimg_mp_opcode1(37,compile(ss5,se1));
11750           if (!std::strncmp(ss,"log10(",6)) _cimg_mp_opcode1(38,compile(ss6,se1));
11751           if (!std::strncmp(ss,"log(",4)) _cimg_mp_opcode1(39,compile(ss4,se1));
11752           if (!std::strncmp(ss,"exp(",4)) _cimg_mp_opcode1(40,compile(ss4,se1));
11753           if (!std::strncmp(ss,"sqrt(",5)) _cimg_mp_opcode1(41,compile(ss5,se1));
11754           if (!std::strncmp(ss,"sign(",5)) _cimg_mp_opcode1(42,compile(ss5,se1));
11755           if (!std::strncmp(ss,"abs(",4)) _cimg_mp_opcode1(43,compile(ss4,se1));
11756           if (!std::strncmp(ss,"atan2(",6)) {
11757             char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
11758             _cimg_mp_opcode2(44,compile(ss6,s1),compile(s1+1,se1));
11759           }
11760           if (!std::strncmp(ss,"if(",3)) {
11761             char *s1 = ss3; while (s1<se4 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
11762             char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
11763             _cimg_mp_opcode3(45,compile(ss3,s1),compile(s1+1,s2),compile(s2+1,se1));
11764           }
11765           if (!std::strncmp(ss,"round(",6)) {
11766             unsigned int value = 0, round = 1, direction = 0;
11767             char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
11768             value = compile(ss6,s1==se2?++s1:s1);
11769             if (s1<se1) {
11770               char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
11771               round = compile(s1+1,s2==se2?++s2:s2);
11772               if (s2<se1) direction = compile(s2+1,se1);
11773             }
11774             _cimg_mp_opcode3(46,value,round,direction);
11775           }
11776           if (!std::strncmp(ss,"?(",2) || !std::strncmp(ss,"u(",2)) {
11777             if (*ss2==')') _cimg_mp_opcode2(0,0,1);
11778             char *s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
11779             if (s1<se1) _cimg_mp_opcode2(0,compile(ss2,s1),compile(s1+1,se1));
11780             _cimg_mp_opcode2(0,0,compile(ss2,s1));
11781           }
11782           if (!std::strncmp(ss,"i(",2)) {
11783             if (*ss2==')') _cimg_mp_return(0);
11784             unsigned int indx = 8, indy = 9, indz = 10, indc = 11, borders = 0;
11785             if (ss2!=se1) {
11786               char *s1 = ss2; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
11787               indx = compile(ss2,s1==se2?++s1:s1);
11788               if (s1<se1) {
11789                 char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
11790                 indy = compile(s1+1,s2==se2?++s2:s2);
11791                 if (s2<se1) {
11792                   char *s3 = s2+1; while (s3<se2 && (*s3!=',' || level[s3-expr._data]!=clevel1)) ++s3;
11793                   indz = compile(s2+1,s3==se2?++s3:s3);
11794                   if (s3<se1) {
11795                     char *s4 = s3+1; while (s4<se2 && (*s4!=',' || level[s4-expr._data]!=clevel1)) ++s4;
11796                     indc = compile(s3+1,s4==se2?++s4:s4);
11797                     if (s4<se1) borders = compile(s4+1,se1);
11798                   }
11799                 }
11800               }
11801             }
11802             _cimg_mp_opcode5(47,indx,indy,indz,indc,borders);
11803           }
11804           if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4)) {
11805             CImgList<uintT> opcode;
11806             if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
11807             const unsigned int pos = mempos++;
11808             CImg<uintT>::vector(ss[1]=='i'?48:49,pos).move_to(opcode);
11809             for (char *s = ss4; s<se; ++s) {
11810               char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
11811               CImg<uintT>::vector(compile(s,ns)).move_to(opcode);
11812               s = ns;
11813             }
11814             (opcode>'y').move_to(code);
11815             _cimg_mp_return(pos);
11816           }
11817           if (!std::strncmp(ss,"narg(",5)) {
11818             if (*ss5==')') _cimg_mp_return(0);
11819             unsigned int nb = 0;
11820             for (char *s = ss5; s<se; ++s) {
11821               char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
11822               ++nb; s = ns;
11823             }
11824             if (nb==0 || nb==1) _cimg_mp_return(nb);
11825             if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
11826             const unsigned int pos = mempos++;
11827             mem[pos] = nb;
11828             _cimg_mp_return(pos);
11829           }
11830           if (!std::strncmp(ss,"isval(",6)) {
11831             char sep = 0, end = 0; double val = 0;
11832             if (std::sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
11833             _cimg_mp_return(0);
11834           }
11835           if (!std::strncmp(ss,"isnan(",6)) _cimg_mp_opcode1(50,compile(ss6,se1));
11836           if (!std::strncmp(ss,"isinf(",6)) _cimg_mp_opcode1(51,compile(ss6,se1));
11837           if (!std::strncmp(ss,"isint(",6)) _cimg_mp_opcode1(52,compile(ss6,se1));
11838           if (!std::strncmp(ss,"isbool(",7)) _cimg_mp_opcode1(53,compile(ss7,se1));
11839           if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) {
11840             unsigned int value = 0, nb = 1;
11841             char *s1 = ss4; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
11842             value = compile(ss4,s1==se2?++s1:s1);
11843             if (s1<se1) {
11844               char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
11845               nb = compile(s1+1,se1);
11846             }
11847             _cimg_mp_opcode2(*ss2=='l'?54:55,value,nb);
11848           }
11849 
11850           if (!std::strncmp(ss,"sinc(",5)) _cimg_mp_opcode1(56,compile(ss5,se1));
11851 
11852         }
11853 
11854         // No known item found, assuming this is an already initialize variable.
11855         CImg<charT> variable_name(ss,se-ss+1); variable_name.back() = 0;
11856         for (unsigned int i = 0; i<mempos; ++i) if (label[i]._data && !std::strcmp(variable_name,label[i])) _cimg_mp_return(i);
11857         *se = saved_char;
11858         throw CImgArgumentException("[_cimg_math_parser] "
11859                                     "CImg<%s>::%s() : Invalid item '%s' in specified expression '%s'.\n",
11860                                     pixel_type(),calling_function,
11861                                     variable_name._data,expr._data);
11862         return 0;
11863       }
11864 
11865       // Evaluation functions, known by the parser.
11866       double mp_u() {
11867         return mem[opcode(2)] + cimg::rand()*(mem[opcode(3)]-mem[opcode(2)]);
11868       }
11869       double mp_g() {
11870         return cimg::grand();
11871       }
11872       double mp_i() {
11873         return (double)reference.atXYZC((int)mem[8],(int)mem[9],(int)mem[10],(int)mem[11],0);
11874       }
11875       double mp_xw() {
11876         return mem[8]/reference.width();
11877       }
11878       double mp_yh() {
11879         return mem[9]/reference.height();
11880       }
11881       double mp_zd() {
11882         return mem[10]/reference.depth();
11883       }
11884       double mp_cs() {
11885         return mem[11]/reference.spectrum();
11886       }
11887       double mp_equal() {
11888         return mem[opcode[2]];
11889       }
11890       double mp_logical_and() {
11891         return (double)((bool)mem[opcode(2)] && (bool)mem[opcode(3)]);
11892       }
11893       double mp_logical_or() {
11894         return (double)((bool)mem[opcode(2)] || (bool)mem[opcode(3)]);
11895       }
11896       double mp_infeq() {
11897         return (double)(mem[opcode(2)] <= mem[opcode(3)]);
11898       }
11899       double mp_supeq() {
11900         return (double)(mem[opcode(2)] >= mem[opcode(3)]);
11901       }
11902       double mp_noteq() {
11903         return (double)(mem[opcode(2)] != mem[opcode(3)]);
11904       }
11905       double mp_eqeq() {
11906         return (double)(mem[opcode(2)] == mem[opcode(3)]);
11907       }
11908       double mp_inf() {
11909         return (double)(mem[opcode(2)] < mem[opcode(3)]);
11910       }
11911       double mp_sup() {
11912         return (double)(mem[opcode(2)] > mem[opcode(3)]);
11913       }
11914       double mp_add() {
11915         return mem[opcode(2)] + mem[opcode(3)];
11916       }
11917       double mp_sub() {
11918         return mem[opcode(2)] - mem[opcode(3)];
11919       }
11920       double mp_mul() {
11921         return mem[opcode(2)] * mem[opcode(3)];
11922       }
11923       double mp_div() {
11924         return mem[opcode(2)] / mem[opcode(3)];
11925       }
11926       double mp_minus() {
11927         return -mem[opcode(2)];
11928       }
11929       double mp_not() {
11930         return !mem[opcode(2)];
11931       }
11932       double mp_logical_not() {
11933         return !mem[opcode(2)];
11934       }
11935       double mp_bitwise_not() {
11936         return ~(unsigned long)mem[opcode(2)];
11937       }
11938       double mp_modulo() {
11939         return cimg::mod(mem[opcode(2)],mem[opcode(3)]);
11940       }
11941       double mp_bitwise_and() {
11942         return ((unsigned long)mem[opcode(2)] & (unsigned long)mem[opcode(3)]);
11943       }
11944       double mp_bitwise_or() {
11945         return ((unsigned long)mem[opcode(2)] | (unsigned long)mem[opcode(3)]);
11946       }
11947       double mp_pow() {
11948         return std::pow(mem[opcode(2)],mem[opcode(3)]);
11949       }
11950       double mp_sin() {
11951         return std::sin(mem[opcode(2)]);
11952       }
11953       double mp_cos() {
11954         return std::cos(mem[opcode(2)]);
11955       }
11956       double mp_tan() {
11957         return std::tan(mem[opcode(2)]);
11958       }
11959       double mp_asin() {
11960         return std::asin(mem[opcode(2)]);
11961       }
11962       double mp_acos() {
11963         return std::acos(mem[opcode(2)]);
11964       }
11965       double mp_atan() {
11966         return std::atan(mem[opcode(2)]);
11967       }
11968       double mp_sinh() {
11969         return std::sinh(mem[opcode(2)]);
11970       }
11971       double mp_cosh() {
11972         return std::cosh(mem[opcode(2)]);
11973       }
11974       double mp_tanh() {
11975         return std::tanh(mem[opcode(2)]);
11976       }
11977       double mp_log10() {
11978         return std::log10(mem[opcode(2)]);
11979       }
11980       double mp_log() {
11981         return std::log(mem[opcode(2)]);
11982       }
11983       double mp_exp() {
11984         return std::exp(mem[opcode(2)]);
11985       }
11986       double mp_sqrt() {
11987         return std::sqrt(mem[opcode(2)]);
11988       }
11989       double mp_sign() {
11990         return cimg::sign(mem[opcode(2)]);
11991       }
11992       double mp_abs() {
11993         return cimg::abs(mem[opcode(2)]);
11994       }
11995       double mp_atan2() {
11996         return std::atan2(mem[opcode(2)],mem[opcode(3)]);
11997       }
11998       double mp_if() {
11999         return mem[opcode(2)]?mem[opcode(3)]:mem[opcode(4)];
12000       }
12001       double mp_round() {
12002         return cimg::round(mem[opcode(2)],mem[opcode(3)],(int)mem[opcode(4)]);
12003       }
12004       double mp_ixyzc() {
12005         const int c = (int)mem[opcode(5)], b = (int)mem[opcode(6)];
12006         if (b==0) return (double)reference.linear_atXYZ((float)mem[opcode(2)],
12007                                                         (float)mem[opcode(3)],
12008                                                         (float)mem[opcode(4)],
12009                                                         c<0?0:c>=reference.spectrum()?reference.spectrum()-1:c,0);
12010         if (b==1) return (double)reference.linear_atXYZ((float)mem[opcode(2)],
12011                                                         (float)mem[opcode(3)],
12012                                                         (float)mem[opcode(4)],
12013                                                         c<0?0:c>=reference.spectrum()?reference.spectrum()-1:c);
12014         return (double)reference.linear_atXYZ((float)cimg::mod(mem[opcode(2)],(double)reference.width()),
12015                                               (float)cimg::mod(mem[opcode(3)],(double)reference.height()),
12016                                               (float)cimg::mod(mem[opcode(4)],(double)reference.depth()),
12017                                               c<0?0:c>=reference.spectrum()?reference.spectrum()-1:c);
12018       }
12019       double mp_min() {
12020         double val = mem[opcode(2)];
12021         for (unsigned int i = 3; i<opcode._height; ++i) val = cimg::min(val,mem[opcode(i)]);
12022         return val;
12023       }
12024       double mp_max() {
12025         double val = mem[opcode(2)];
12026         for (unsigned int i = 3; i<opcode._height; ++i) val = cimg::max(val,mem[opcode(i)]);
12027         return val;
12028       }
12029       double mp_isnan() {
12030         const double val = mem[opcode(2)];
12031         return !(val == val);
12032       }
12033       double mp_isinf() {
12034         const double val = mem[opcode(2)];
12035         return val == (val+1);
12036       }
12037       double mp_isint() {
12038         const double val = mem[opcode(2)];
12039         return (double)(int)val == val;
12040       }
12041       double mp_isbool() {
12042         const double val = mem[opcode(2)];
12043         return (val==0.0 || val==1.0);
12044       }
12045       double mp_rol() {
12046         return cimg::rol(mem[opcode(2)],(unsigned int)mem[opcode(3)]);
12047       }
12048       double mp_ror() {
12049         return cimg::ror(mem[opcode(2)],(unsigned int)mem[opcode(3)]);
12050       }
12051       double mp_lsl() {
12052         return (long)mem[opcode(2)]<<(unsigned int)mem[opcode(3)];
12053       }
12054       double mp_lsr() {
12055         return (long)mem[opcode(2)]>>(unsigned int)mem[opcode(3)];
12056       }
12057       double mp_sinc() {
12058         return cimg::sinc(mem[opcode(2)]);
12059       }
12060       double mp_im() {
12061         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12062         return reference_stats?reference_stats[0]:0;
12063       }
12064       double mp_iM() {
12065         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12066         return reference_stats?reference_stats[1]:0;
12067       }
12068       double mp_ia() {
12069         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12070         return reference_stats?reference_stats[2]:0;
12071       }
12072       double mp_iv() {
12073         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12074         return reference_stats?reference_stats[3]:0;
12075       }
12076       double mp_xm() {
12077         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12078         return reference_stats?reference_stats[4]:0;
12079       }
12080       double mp_ym() {
12081         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12082         return reference_stats?reference_stats[5]:0;
12083       }
12084       double mp_zm() {
12085         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12086         return reference_stats?reference_stats[6]:0;
12087       }
12088       double mp_cm() {
12089         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12090         return reference_stats?reference_stats[7]:0;
12091       }
12092       double mp_xM() {
12093         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12094         return reference_stats?reference_stats[8]:0;
12095       }
12096       double mp_yM() {
12097         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12098         return reference_stats?reference_stats[9]:0;
12099       }
12100       double mp_zM() {
12101         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12102         return reference_stats?reference_stats[10]:0;
12103       }
12104       double mp_cM() {
12105         if (!reference_stats) reference.get_stats().move_to(reference_stats);
12106         return reference_stats?reference_stats[11]:0;
12107       }
12108 
12109       // Evaluation procedure, with image data.
12110       double eval(const double x, const double y, const double z, const double c) {
12111         typedef double (_cimg_math_parser::*mp_func)();
12112         const mp_func mp_funcs[] = {
12113           &_cimg_math_parser::mp_u,            // 0
12114           &_cimg_math_parser::mp_g,            // 1
12115           &_cimg_math_parser::mp_i,            // 2
12116           &_cimg_math_parser::mp_xw,           // 3
12117           &_cimg_math_parser::mp_yh,           // 4
12118           &_cimg_math_parser::mp_zd,           // 5
12119           &_cimg_math_parser::mp_cs,           // 6
12120           &_cimg_math_parser::mp_equal,        // 7
12121           &_cimg_math_parser::mp_logical_or,   // 8
12122           &_cimg_math_parser::mp_logical_and,  // 9
12123           &_cimg_math_parser::mp_bitwise_or,   // 10
12124           &_cimg_math_parser::mp_bitwise_and,  // 11
12125           &_cimg_math_parser::mp_noteq,        // 12
12126           &_cimg_math_parser::mp_eqeq,         // 13
12127           &_cimg_math_parser::mp_infeq,        // 14
12128           &_cimg_math_parser::mp_supeq,        // 15
12129           &_cimg_math_parser::mp_inf,          // 16
12130           &_cimg_math_parser::mp_sup,          // 17
12131           &_cimg_math_parser::mp_lsl,          // 18
12132           &_cimg_math_parser::mp_lsr,          // 19
12133           &_cimg_math_parser::mp_sub,          // 20
12134           &_cimg_math_parser::mp_add,          // 21
12135           &_cimg_math_parser::mp_mul,          // 22
12136           &_cimg_math_parser::mp_div,          // 23
12137           &_cimg_math_parser::mp_modulo,       // 24
12138           &_cimg_math_parser::mp_pow,          // 25
12139           &_cimg_math_parser::mp_minus,        // 26
12140           &_cimg_math_parser::mp_logical_not,  // 27
12141           &_cimg_math_parser::mp_bitwise_not,  // 28
12142           &_cimg_math_parser::mp_sin,          // 29
12143           &_cimg_math_parser::mp_cos,          // 30
12144           &_cimg_math_parser::mp_tan,          // 31
12145           &_cimg_math_parser::mp_asin,         // 32
12146           &_cimg_math_parser::mp_acos,         // 33
12147           &_cimg_math_parser::mp_atan,         // 34
12148           &_cimg_math_parser::mp_sinh,         // 35
12149           &_cimg_math_parser::mp_cosh,         // 36
12150           &_cimg_math_parser::mp_tanh,         // 37
12151           &_cimg_math_parser::mp_log10,        // 38
12152           &_cimg_math_parser::mp_log,          // 39
12153           &_cimg_math_parser::mp_exp,          // 40
12154           &_cimg_math_parser::mp_sqrt,         // 41
12155           &_cimg_math_parser::mp_sign,         // 42
12156           &_cimg_math_parser::mp_abs,          // 43
12157           &_cimg_math_parser::mp_atan2,        // 44
12158           &_cimg_math_parser::mp_if,           // 45
12159           &_cimg_math_parser::mp_round,        // 46
12160           &_cimg_math_parser::mp_ixyzc,        // 47
12161           &_cimg_math_parser::mp_min,          // 48
12162           &_cimg_math_parser::mp_max,          // 49
12163           &_cimg_math_parser::mp_isnan,        // 50
12164           &_cimg_math_parser::mp_isinf,        // 51
12165           &_cimg_math_parser::mp_isint,        // 52
12166           &_cimg_math_parser::mp_isbool,       // 53
12167           &_cimg_math_parser::mp_rol,          // 54
12168           &_cimg_math_parser::mp_ror,          // 55
12169           &_cimg_math_parser::mp_sinc,         // 56
12170           &_cimg_math_parser::mp_im,           // 57
12171           &_cimg_math_parser::mp_iM,           // 58
12172           &_cimg_math_parser::mp_ia,           // 59
12173           &_cimg_math_parser::mp_iv,           // 60
12174           &_cimg_math_parser::mp_xm,           // 61
12175           &_cimg_math_parser::mp_ym,           // 62
12176           &_cimg_math_parser::mp_zm,           // 63
12177           &_cimg_math_parser::mp_cm,           // 64
12178           &_cimg_math_parser::mp_xM,           // 65
12179           &_cimg_math_parser::mp_yM,           // 66
12180           &_cimg_math_parser::mp_zM,           // 67
12181           &_cimg_math_parser::mp_cM            // 68
12182         };
12183 
12184         if (!mem) return 0;
12185         mem[8] = x; mem[9] = y; mem[10] = z; mem[11] = c;
12186         opcode._is_shared = true; opcode._width = opcode._depth = opcode._spectrum = 1;
12187         cimglist_for(code,l) {
12188           const CImg<uintT> &op = code[l];
12189           opcode._data = op._data; opcode._height = op._height;  // Allows to avoid parameter passing to evaluation functions.
12190           mem[opcode(1)] = (this->*mp_funcs[opcode[0]])();
12191         }
12192         return mem[result];
12193       }
12194     };
12195 
12196     //! Compute the square value of each pixel.
12197     CImg<T>& sqr() {
12198       cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); };
12199       return *this;
12200     }
12201 
12202     CImg<Tfloat> get_sqr() const {
12203       return CImg<Tfloat>(*this,false).sqr();
12204     }
12205 
12206     //! Compute the square root of each pixel value.
12207     CImg<T>& sqrt() {
12208       cimg_for(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd);
12209       return *this;
12210     }
12211 
12212     CImg<Tfloat> get_sqrt() const {
12213       return CImg<Tfloat>(*this,false).sqrt();
12214     }
12215 
12216     //! Compute the exponential of each pixel value.
12217     CImg<T>& exp() {
12218       cimg_for(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd);
12219       return *this;
12220     }
12221 
12222     CImg<Tfloat> get_exp() const {
12223       return CImg<Tfloat>(*this,false).exp();
12224     }
12225 
12226     //! Compute the log of each each pixel value.
12227     CImg<T>& log() {
12228       cimg_for(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd);
12229       return *this;
12230     }
12231 
12232     CImg<Tfloat> get_log() const {
12233       return CImg<Tfloat>(*this,false).log();
12234     }
12235 
12236     //! Compute the log10 of each each pixel value.
12237     CImg<T>& log10() {
12238       cimg_for(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd);
12239       return *this;
12240     }
12241 
12242     CImg<Tfloat> get_log10() const {
12243       return CImg<Tfloat>(*this,false).log10();
12244     }
12245 
12246     //! Compute the absolute value of each pixel value.
12247     CImg<T>& abs() {
12248       cimg_for(*this,ptrd,T) *ptrd = cimg::abs(*ptrd);
12249       return *this;
12250     }
12251 
12252     CImg<Tfloat> get_abs() const {
12253       return CImg<Tfloat>(*this,false).abs();
12254     }
12255 
12256     //! Compute the sign of each pixel value.
12257     CImg<T>& sign() {
12258       cimg_for(*this,ptrd,T) *ptrd = cimg::sign(*ptrd);
12259       return *this;
12260     }
12261 
12262     CImg<Tfloat> get_sign() const {
12263       return CImg<Tfloat>(*this,false).sign();
12264     }
12265 
12266     //! Compute the cosinus of each pixel value.
12267     CImg<T>& cos() {
12268       cimg_for(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd);
12269       return *this;
12270     }
12271 
12272     CImg<Tfloat> get_cos() const {
12273       return CImg<Tfloat>(*this,false).cos();
12274     }
12275 
12276     //! Compute the sinus of each pixel value.
12277     CImg<T>& sin() {
12278       cimg_for(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd);
12279       return *this;
12280     }
12281 
12282     CImg<Tfloat> get_sin() const {
12283       return CImg<Tfloat>(*this,false).sin();
12284     }
12285 
12286     //! Compute the sinus cardinal of each pixel value.
12287     CImg<T>& sinc() {
12288       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd);
12289       return *this;
12290     }
12291 
12292     CImg<Tfloat> get_sinc() const {
12293       return CImg<Tfloat>(*this,false).sinc();
12294     }
12295 
12296     //! Compute the tangent of each pixel.
12297     CImg<T>& tan() {
12298       cimg_for(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd);
12299       return *this;
12300     }
12301 
12302     CImg<Tfloat> get_tan() const {
12303       return CImg<Tfloat>(*this,false).tan();
12304     }
12305 
12306     //! Compute the hyperbolic cosine of each pixel value.
12307     CImg<T>& cosh() {
12308       cimg_for(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd);
12309       return *this;
12310     }
12311 
12312     CImg<Tfloat> get_cosh() const {
12313       return CImg<Tfloat>(*this,false).cosh();
12314     }
12315 
12316     //! Compute the hyperbolic sine of each pixel value.
12317     CImg<T>& sinh() {
12318       cimg_for(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd);
12319       return *this;
12320     }
12321 
12322     CImg<Tfloat> get_sinh() const {
12323       return CImg<Tfloat>(*this,false).sinh();
12324     }
12325 
12326     //! Compute the hyperbolic tangent of each pixel value.
12327     CImg<T>& tanh() {
12328       cimg_for(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd);
12329       return *this;
12330     }
12331 
12332     CImg<Tfloat> get_tanh() const {
12333       return CImg<Tfloat>(*this,false).tanh();
12334     }
12335 
12336     //! Compute the arc-cosine of each pixel value.
12337     CImg<T>& acos() {
12338       cimg_for(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd);
12339       return *this;
12340     }
12341 
12342     CImg<Tfloat> get_acos() const {
12343       return CImg<Tfloat>(*this,false).acos();
12344     }
12345 
12346     //! Compute the arc-sinus of each pixel value.
12347     CImg<T>& asin() {
12348       cimg_for(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd);
12349       return *this;
12350     }
12351 
12352     CImg<Tfloat> get_asin() const {
12353       return CImg<Tfloat>(*this,false).asin();
12354     }
12355 
12356     //! Compute the arc-tangent of each pixel.
12357     CImg<T>& atan() {
12358       cimg_for(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd);
12359       return *this;
12360     }
12361 
12362     CImg<Tfloat> get_atan() const {
12363       return CImg<Tfloat>(*this,false).atan();
12364     }
12365 
12366     //! Compute the arc-tangent of each pixel.
12367     template<typename t>
12368     CImg<T>& atan2(const CImg<t>& img) {
12369       const unsigned int smin = cimg::min(size(),img.size());
12370       t *ptrs = img._data + smin;
12371       for (T *ptrd = _data + smin; ptrd>_data; --ptrd, *ptrd = (T)std::atan2((double)*ptrd,(double)*(--ptrs))) {}
12372       return *this;
12373     }
12374 
12375     template<typename t>
12376     CImg<Tfloat> get_atan2(const CImg<t>& img) const {
12377       return CImg<Tfloat>(*this,false).atan2(img);
12378     }
12379 
12380     //! Pointwise multiplication between an image and an expression.
12381     CImg<T>& mul(const char *const expression) {
12382       const unsigned int omode = cimg::exception_mode();
12383       cimg::exception_mode() = 0;
12384       try {
12385         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
12386         _cimg_math_parser mp(base,expression,"mul");
12387         T *ptrd = _data;
12388         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp.eval(x,y,z,c)); ++ptrd; }
12389       } catch (CImgException&) {
12390         cimg::exception_mode() = omode;
12391         CImg<T> values(_width,_height,_depth,_spectrum);
12392         values = expression;
12393         mul(values);
12394       }
12395       cimg::exception_mode() = omode;
12396       return *this;
12397     }
12398 
12399     //! Pointwise multiplication between two images.
12400     template<typename t>
12401     CImg<T>& mul(const CImg<t>& img) {
12402       const unsigned int siz = size(), isiz = img.size();
12403       if (siz && isiz) {
12404         if (is_overlapped(img)) return mul(+img);
12405         T *ptrd = _data, *const ptre = _data + siz;
12406         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
12407           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
12408         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
12409       }
12410       return *this;
12411     }
12412 
12413     template<typename t>
12414     CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
12415       return CImg<_cimg_Tt>(*this,false).mul(img);
12416     }
12417 
12418     //! Pointwise division between an image and an expression.
12419     CImg<T>& div(const char *const expression) {
12420       const unsigned int omode = cimg::exception_mode();
12421       cimg::exception_mode() = 0;
12422       try {
12423         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
12424         _cimg_math_parser mp(base,expression,"div");
12425         T *ptrd = _data;
12426         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp.eval(x,y,z,c)); ++ptrd; }
12427       } catch (CImgException&) {
12428         cimg::exception_mode() = omode;
12429         CImg<T> values(_width,_height,_depth,_spectrum);
12430         values = expression;
12431         div(values);
12432       }
12433       cimg::exception_mode() = omode;
12434       return *this;
12435     }
12436 
12437     //! Pointwise division between two images.
12438     template<typename t>
12439     CImg<T>& div(const CImg<t>& img) {
12440       const unsigned int siz = size(), isiz = img.size();
12441       if (siz && isiz) {
12442         if (is_overlapped(img)) return div(+img);
12443         T *ptrd = _data, *const ptre = _data + siz;
12444         if (siz>isiz) for (unsigned int n = siz/isiz; n; --n)
12445           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
12446         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
12447       }
12448       return *this;
12449     }
12450 
12451     template<typename t>
12452     CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
12453       return CImg<_cimg_Tt>(*this,false).div(img);
12454     }
12455 
12456     //! Compute the power by p of each pixel value.
12457     CImg<T>& pow(const double p) {
12458       if (p==0) return fill(1);
12459       if (p==0.5) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)std::sqrt((double)val); } return *this; }
12460       if (p==1) return *this;
12461       if (p==2) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val; } return *this; }
12462       if (p==3) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } return *this; }
12463       if (p==4) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } return *this; }
12464       cimg_for(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p);
12465       return *this;
12466     }
12467 
12468     CImg<Tfloat> get_pow(const double p) const {
12469       return CImg<Tfloat>(*this,false).pow(p);
12470     }
12471 
12472     //! Compute the power of each pixel value.
12473     template<typename t>
12474     CImg<T>& pow(const CImg<t>& img) {
12475       if (is_overlapped(img)) return pow(+img);
12476       t *ptrs = img._data;
12477       T *ptrf = _data + cimg::min(size(),img.size());
12478       for (T* ptrd = _data; ptrd<ptrf; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
12479       return *this;
12480     }
12481 
12482     template<typename t>
12483     CImg<Tfloat> get_pow(const CImg<t>& img) const {
12484       return CImg<Tfloat>(*this,false).pow(img);
12485     }
12486 
12487     //! Compute the power of each pixel value.
12488     CImg<T>& pow(const char *const expression) {
12489       const unsigned int omode = cimg::exception_mode();
12490       cimg::exception_mode() = 0;
12491       try {
12492         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
12493         _cimg_math_parser mp(base,expression,"pow");
12494         T *ptrd = _data;
12495         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp.eval(x,y,z,c)); ++ptrd; }
12496       } catch (CImgException&) {
12497         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
12498         try {
12499           values.fill(expression,true);
12500         } catch (CImgException&) {
12501           cimg::exception_mode() = omode;
12502           values.load(expression);
12503         }
12504         pow(values);
12505       }
12506       cimg::exception_mode() = omode;
12507       return *this;
12508     }
12509 
12510     CImg<Tfloat> get_pow(const char *const expression) const {
12511       return CImg<Tfloat>(*this,false).pow(expression);
12512     }
12513 
12514     //! Compute the bitwise left rotation of each pixel value.
12515     CImg<T>& rol(const unsigned int n=1) {
12516       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n);
12517       return *this;
12518     }
12519 
12520     CImg<T> get_rol(const unsigned int n=1) const {
12521       return (+*this).rol(n);
12522     }
12523 
12524     template<typename t>
12525     CImg<T>& rol(const CImg<t>& img) {
12526       if (is_overlapped(img)) return rol(+img);
12527       t *ptrs = img._data;
12528       T *ptrf = _data + cimg::min(size(),img.size());
12529       for (T* ptrd = _data; ptrd<ptrf; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
12530       return *this;
12531     }
12532 
12533     template<typename t>
12534     CImg<T> get_rol(const CImg<t>& img) const {
12535       return (+*this).rol(img);
12536     }
12537 
12538     CImg<T>& rol(const char *const expression) {
12539       const unsigned int omode = cimg::exception_mode();
12540       cimg::exception_mode() = 0;
12541       try {
12542         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
12543         _cimg_math_parser mp(base,expression,"rol");
12544         T *ptrd = _data;
12545         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; }
12546       } catch (CImgException&) {
12547         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
12548         try {
12549           values.fill(expression,true);
12550         } catch (CImgException&) {
12551           cimg::exception_mode() = omode;
12552           values.load(expression);
12553         }
12554         rol(values);
12555       }
12556       cimg::exception_mode() = omode;
12557       return *this;
12558     }
12559 
12560     CImg<T> get_rol(const char *const expression) const {
12561       return (+*this).rol(expression);
12562     }
12563 
12564     //! Compute the bitwise right rotation of each pixel value.
12565     CImg<T>& ror(const unsigned int n=1) {
12566       cimg_for(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n);
12567       return *this;
12568     }
12569 
12570     CImg<T> get_ror(const unsigned int n=1) const {
12571       return (+*this).ror(n);
12572     }
12573 
12574     template<typename t>
12575     CImg<T>& ror(const CImg<t>& img) {
12576       if (is_overlapped(img)) return ror(+img);
12577       t *ptrs = img._data;
12578       T *ptrf = _data + cimg::min(size(),img.size());
12579       for (T* ptrd = _data; ptrd<ptrf; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
12580       return *this;
12581     }
12582 
12583     template<typename t>
12584     CImg<T> get_ror(const CImg<t>& img) const {
12585       return (+*this).ror(img);
12586     }
12587 
12588     CImg<T>& ror(const char *const expression) {
12589       const unsigned int omode = cimg::exception_mode();
12590       cimg::exception_mode() = 0;
12591       try {
12592         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
12593         _cimg_math_parser mp(base,expression,"ror");
12594         T *ptrd = _data;
12595         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; }
12596       } catch (CImgException&) {
12597         CImg<Tfloat> values(_width,_height,_depth,_spectrum);
12598         try {
12599           values.fill(expression,true);
12600         } catch (CImgException&) {
12601           cimg::exception_mode() = omode;
12602           values.load(expression);
12603         }
12604         ror(values);
12605       }
12606       cimg::exception_mode() = omode;
12607       return *this;
12608     }
12609 
12610     CImg<T> get_ror(const char *const expression) const {
12611       return (+*this).ror(expression);
12612     }
12613 
12614     //! Pointwise min operator between an image and a value.
12615     CImg<T>& min(const T val) {
12616       cimg_for(*this,ptrd,T) *ptrd = cimg::min(*ptrd,val);
12617       return *this;
12618     }
12619 
12620     CImg<T> get_min(const T val) const {
12621       return (+*this).min(val);
12622     }
12623 
12624     //! Pointwise min operator between two images.
12625     template<typename t>
12626     CImg<T>& min(const CImg<t>& img) {
12627       if (is_overlapped(img)) return min(+img);
12628       t *ptrs = img._data;
12629       T *ptrf = _data + cimg::min(size(),img.size());
12630       for (T* ptrd = _data; ptrd<ptrf; ++ptrd) *ptrd = cimg::min((T)*(ptrs++),*ptrd);
12631       return *this;
12632     }
12633 
12634     template<typename t>
12635     CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
12636       return CImg<_cimg_Tt>(*this,false).min(img);
12637     }
12638 
12639     //! Pointwise min operator between an image and a string.
12640     CImg<T>& min(const char *const expression) {
12641       const unsigned int omode = cimg::exception_mode();
12642       cimg::exception_mode() = 0;
12643       try {
12644         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
12645         _cimg_math_parser mp(base,expression,"min");
12646         T *ptrd = _data;
12647         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
12648       } catch (CImgException&) {
12649         CImg<T> values(_width,_height,_depth,_spectrum);
12650         try {
12651           values.fill(expression,true);
12652         } catch (CImgException&) {
12653           cimg::exception_mode() = omode;
12654           values.load(expression);
12655         }
12656         min(values);
12657       }
12658       cimg::exception_mode() = omode;
12659       return *this;
12660     }
12661 
12662     CImg<Tfloat> get_min(const char *const expression) const {
12663       return CImg<Tfloat>(*this,false).min(expression);
12664     }
12665 
12666     //! Pointwise max operator between an image and a value.
12667     CImg<T>& max(const T val) {
12668       cimg_for(*this,ptrd,T) *ptrd = cimg::max(*ptrd,val);
12669       return *this;
12670     }
12671 
12672     CImg<T> get_max(const T val) const {
12673       return (+*this).max(val);
12674     }
12675 
12676     //! Pointwise max operator between two images.
12677     template<typename t>
12678     CImg<T>& max(const CImg<t>& img) {
12679       if (is_overlapped(img)) return max(+img);
12680       t *ptrs = img._data;
12681       T *ptrf = _data + cimg::min(size(),img.size());
12682       for (T* ptrd = _data; ptrd<ptrf; ++ptrd) *ptrd = cimg::max((T)*(ptrs++),*ptrd);
12683       return *this;
12684     }
12685 
12686     template<typename t>
12687     CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
12688       return CImg<_cimg_Tt>(*this,false).max(img);
12689     }
12690 
12691     //! Pointwise max operator between an image and a string.
12692     CImg<T>& max(const char *const expression) {
12693       const unsigned int omode = cimg::exception_mode();
12694       cimg::exception_mode() = 0;
12695       try {
12696         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
12697         _cimg_math_parser mp(base,expression,"max");
12698         T *ptrd = _data;
12699         cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
12700       } catch (CImgException&) {
12701         CImg<T> values(_width,_height,_depth,_spectrum);
12702         try {
12703           values.fill(expression,true);
12704         } catch (CImgException&) {
12705           cimg::exception_mode() = omode;
12706           values.load(expression);
12707         }
12708         max(values);
12709       }
12710       cimg::exception_mode() = omode;
12711       return *this;
12712     }
12713 
12714     CImg<Tfloat> get_max(const char *const expression) const {
12715       return CImg<Tfloat>(*this,false).max(expression);
12716     }
12717 
12718     //! Return a reference to the minimum pixel value of the instance image
12719     T& min() {
12720       if (is_empty())
12721         throw CImgInstanceException(_cimg_instance
12722                                     "min() : Empty instance.",
12723                                     cimg_instance);
12724       T *ptr_min = _data;
12725       T min_value = *ptr_min;
12726       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
12727       return *ptr_min;
12728     }
12729 
12730     const T& min() const {
12731       if (is_empty())
12732         throw CImgInstanceException(_cimg_instance
12733                                     "min() : Empty instance.",
12734                                     cimg_instance);
12735       const T *ptr_min = _data;
12736       T min_value = *ptr_min;
12737       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
12738       return *ptr_min;
12739     }
12740 
12741     //! Return a reference to the maximum pixel value of the instance image
12742     T& max() {
12743       if (is_empty())
12744         throw CImgInstanceException(_cimg_instance
12745                                     "max() : Empty instance.",
12746                                     cimg_instance);
12747       T *ptr_max = _data;
12748       T max_value = *ptr_max;
12749       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
12750       return *ptr_max;
12751     }
12752 
12753     const T& max() const {
12754       if (is_empty())
12755         throw CImgInstanceException(_cimg_instance
12756                                     "max() : Empty instance.",
12757                                     cimg_instance);
12758       const T *ptr_max = _data;
12759       T max_value = *ptr_max;
12760       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
12761       return *ptr_max;
12762     }
12763 
12764     //! Return a reference to the minimum pixel value and return also the maximum pixel value.
12765     template<typename t>
12766     T& min_max(t& max_val) {
12767       if (is_empty())
12768         throw CImgInstanceException(_cimg_instance
12769                                     "min_max() : Empty instance.",
12770                                     cimg_instance);
12771       T *ptr_min = _data;
12772       T min_value = *ptr_min, max_value = min_value;
12773       cimg_for(*this,ptrs,T) {
12774         const T val = *ptrs;
12775         if (val<min_value) { min_value = val; ptr_min = ptrs; }
12776         if (val>max_value) max_value = val;
12777       }
12778       max_val = (t)max_value;
12779       return *ptr_min;
12780     }
12781 
12782     template<typename t>
12783     const T& min_max(t& max_val) const {
12784       if (is_empty())
12785         throw CImgInstanceException(_cimg_instance
12786                                     "min_max() : Empty instance.",
12787                                     cimg_instance);
12788       const T *ptr_min = _data;
12789       T min_value = *ptr_min, max_value = min_value;
12790       cimg_for(*this,ptrs,T) {
12791         const T val = *ptrs;
12792         if (val<min_value) { min_value = val; ptr_min = ptrs; }
12793         if (val>max_value) max_value = val;
12794       }
12795       max_val = (t)max_value;
12796       return *ptr_min;
12797     }
12798 
12799     //! Return a reference to the maximum pixel value and return also the minimum pixel value.
12800     template<typename t>
12801     T& max_min(t& min_val) {
12802       if (is_empty())
12803         throw CImgInstanceException(_cimg_instance
12804                                     "max_min() : Empty instance.",
12805                                     cimg_instance);
12806       T *ptr_max = _data;
12807       T max_value = *ptr_max, min_value = max_value;
12808       cimg_for(*this,ptrs,T) {
12809         const T val = *ptrs;
12810         if (val>max_value) { max_value = val; ptr_max = ptrs; }
12811         if (val<min_value) min_value = val;
12812       }
12813       min_val = (t)min_value;
12814       return *ptr_max;
12815     }
12816 
12817     template<typename t>
12818     const T& max_min(t& min_val) const {
12819       if (is_empty())
12820         throw CImgInstanceException(_cimg_instance
12821                                     "max_min() : Empty instance.",
12822                                     cimg_instance);
12823       const T *ptr_max = _data;
12824       T max_value = *ptr_max, min_value = max_value;
12825       cimg_for(*this,ptrs,T) {
12826         const T val = *ptrs;
12827         if (val>max_value) { max_value = val; ptr_max = ptrs; }
12828         if (val<min_value) min_value = val;
12829       }
12830       min_val = (t)min_value;
12831       return *ptr_max;
12832     }
12833 
12834     //! Return the kth smallest element of the image.
12835     T kth_smallest(const unsigned int k) const {
12836       if (is_empty())
12837         throw CImgInstanceException(_cimg_instance
12838                                     "kth_smallest() : Empty instance.",
12839                                     cimg_instance);
12840       CImg<T> arr(*this);
12841       unsigned int l = 0, ir = size() - 1;
12842       for (;;) {
12843         if (ir<=l+1) {
12844           if (ir==l+1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
12845           return arr[k];
12846         } else {
12847           const unsigned int mid = (l + ir)>>1;
12848           cimg::swap(arr[mid],arr[l+1]);
12849           if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
12850           if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]);
12851           if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]);
12852           unsigned int i = l + 1, j = ir;
12853           const T pivot = arr[l+1];
12854           for (;;) {
12855             do ++i; while (arr[i]<pivot);
12856             do --j; while (arr[j]>pivot);
12857             if (j<i) break;
12858             cimg::swap(arr[i],arr[j]);
12859           }
12860           arr[l+1] = arr[j];
12861           arr[j] = pivot;
12862           if (j>=k) ir = j - 1;
12863           if (j<=k) l = i;
12864         }
12865       }
12866       return 0;
12867     }
12868 
12869     //! Return the median value of the image.
12870     T median() const {
12871       const unsigned int s = size();
12872       const T res = kth_smallest(s>>1);
12873       return (s%2)?res:((res+kth_smallest((s>>1)-1))/2);
12874     }
12875 
12876     //! Return the sum of all the pixel values in an image.
12877     Tdouble sum() const {
12878       if (is_empty())
12879         throw CImgInstanceException(_cimg_instance
12880                                     "sum() : Empty instance.",
12881                                     cimg_instance);
12882       Tdouble res = 0;
12883       cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
12884       return res;
12885     }
12886 
12887     //! Return the mean pixel value of the instance image.
12888     Tdouble mean() const {
12889       if (is_empty())
12890         throw CImgInstanceException(_cimg_instance
12891                                     "mean() : Empty instance.",
12892                                     cimg_instance);
12893       Tdouble res = 0;
12894       cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
12895       return res/size();
12896     }
12897 
12898     //! Return the variance of the image.
12899     /**
12900        @param variance_method Determines how to calculate the variance
12901        <table border="0">
12902        <tr><td>0</td>
12903        <td>Second moment:
12904        @f$ v = 1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2
12905        = 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right) @f$
12906        with @f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$</td></tr>
12907        <tr><td>1</td>
12908        <td>Best unbiased estimator: @f$ v = \frac{1}{N-1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 @f$</td></tr>
12909        <tr><td>2</td>
12910        <td>Least median of squares</td></tr>
12911        <tr><td>3</td>
12912        <td>Least trimmed of squares</td></tr>
12913        </table>
12914     */
12915     Tdouble variance(const unsigned int variance_method=1) const {
12916       Tdouble foo;
12917       return variance_mean(variance_method,foo);
12918     }
12919 
12920     //! Return the variance and the mean of the image.
12921     template<typename t>
12922     Tdouble variance_mean(const unsigned int variance_method, t& mean) const {
12923       if (is_empty())
12924         throw CImgInstanceException(_cimg_instance
12925                                     "variance() : Empty instance.",
12926                                     cimg_instance);
12927 
12928       Tdouble variance = 0, average = 0;
12929       const unsigned int siz = size();
12930       switch (variance_method) {
12931       case 0 :{ // Least mean square (standard definition)
12932         Tdouble S = 0, S2 = 0;
12933         cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
12934         variance = (S2 - S*S/siz)/siz;
12935         average = S;
12936       } break;
12937       case 1 : { // Least mean square (robust definition)
12938         Tdouble S = 0, S2 = 0;
12939         cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
12940         variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
12941         average = S;
12942       } break;
12943       case 2 : { // Least Median of Squares (MAD)
12944         CImg<Tfloat> buf(*this);
12945         buf.sort();
12946         const unsigned int siz2 = siz>>1;
12947         const Tdouble med_i = (double)buf[siz2];
12948         cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; }
12949         buf.sort();
12950         const Tdouble sig = (Tdouble)(1.4828*buf[siz2]);
12951         variance = sig*sig;
12952       } break;
12953       default : { // Least trimmed of Squares
12954         CImg<Tfloat> buf(*this);
12955         const unsigned int siz2 = siz>>1;
12956         cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; }
12957         buf.sort();
12958         Tdouble a = 0;
12959         const Tfloat *ptrs = buf._data;
12960         for (unsigned int j = 0; j<siz2; ++j) a+=(Tdouble)*(ptrs++);
12961         const Tdouble sig = (Tdouble)(2.6477*std::sqrt(a/siz2));
12962         variance = sig*sig;
12963       }
12964       }
12965       mean = (t)(average/siz);
12966       return variance>0?variance:0;
12967     }
12968 
12969     //! Estimate noise variance of the instance image.
12970     Tdouble variance_noise(const unsigned int variance_method=1) const {
12971       const unsigned int siz = size();
12972       if (!siz || !_data) return 0;
12973       if (variance_method>1) return get_laplacian().variance(variance_method);
12974       Tdouble variance = 0, S = 0, S2 = 0;
12975       if (_depth==1) {
12976         CImg_3x3(I,T);
12977         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
12978           const Tdouble val = (Tdouble)Inc + (Tdouble)Ipc + (Tdouble)Icn + (Tdouble)Icp - 4*(Tdouble)Icc;
12979           S+=val; S2+=val*val;
12980         }
12981       } else {
12982         CImg_3x3x3(I,T);
12983         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
12984           const Tdouble val =
12985             (Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc + (Tdouble)Icpc +
12986             (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc;
12987           S+=val; S2+=val*val;
12988         }
12989       }
12990       if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
12991       else variance = (S2 - S*S/siz)/siz;
12992       return variance>0?variance:0;
12993     }
12994 
12995     //! Compute the MSE (Mean-Squared Error) between two images.
12996     template<typename t>
12997     Tdouble MSE(const CImg<t>& img) const {
12998       if (img.size()!=size())
12999         throw CImgArgumentException(_cimg_instance
13000                                     "MSE() : Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
13001                                     cimg_instance,
13002                                     img._width,img._height,img._depth,img._spectrum,img._data);
13003       Tdouble vMSE = 0;
13004       const t* ptr2 = img.end();
13005       cimg_for(*this,ptr1,T) {
13006         const Tdouble diff = (Tdouble)*ptr1 - (Tdouble)*(--ptr2);
13007         vMSE+=diff*diff;
13008       }
13009       vMSE/=img.size();
13010       return vMSE;
13011     }
13012 
13013     //! Compute the PSNR between two images.
13014     template<typename t>
13015     Tdouble PSNR(const CImg<t>& img, const Tdouble valmax=255) const {
13016       const Tdouble vMSE = (Tdouble)std::sqrt(MSE(img));
13017       return (vMSE!=0)?(Tdouble)(20*std::log10(valmax/vMSE)):(Tdouble)(cimg::type<Tdouble>::max());
13018     }
13019 
13020     //! Evaluate math expression.
13021     /**
13022        If you make successive evaluations on the same image and with the same expression,
13023        you can set 'expr' to 0 after the first call, to skip the math parsing step.
13024     **/
13025     double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0) const {
13026       static _cimg_math_parser *mp = 0;
13027       if (expression) { if (mp) delete mp; mp = 0; mp = new _cimg_math_parser(*this,expression,"eval"); }
13028       if (!mp)
13029         throw CImgArgumentException(_cimg_instance
13030                                     "eval() : No expression has been previously specified.",
13031                                     cimg_instance);
13032       return mp->eval(x,y,z,c);
13033     }
13034 
13035     //! Compute a statistics vector (min,max,mean,variance,xmin,ymin,zmin,cmin,xmax,ymax,zmax,cmax).
13036     CImg<T>& stats(const unsigned int variance_method=1) {
13037       return get_stats(variance_method).move_to(*this);
13038     }
13039 
13040     CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
13041       if (is_empty()) return CImg<doubleT>();
13042       const unsigned int siz = size();
13043       const T *const odata = _data;
13044       const T *pm = odata, *pM = odata;
13045       Tdouble S = 0, S2 = 0;
13046       T m = *pm, M = m;
13047       cimg_for(*this,ptrs,T) {
13048         const T val = *ptrs;
13049         const Tdouble _val = (Tdouble)val;
13050         if (val<m) { m = val; pm = ptrs; }
13051         if (val>M) { M = val; pM = ptrs; }
13052         S+=_val;
13053         S2+=_val*_val;
13054       }
13055       const Tdouble
13056         mean_value = S/siz,
13057         _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
13058         (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
13059          variance(variance_method)),
13060         variance_value = _variance_value>0?_variance_value:0;
13061       int
13062         xm = 0, ym = 0, zm = 0, cm = 0,
13063         xM = 0, yM = 0, zM = 0, cM = 0;
13064       contains(*pm,xm,ym,zm,cm);
13065       contains(*pM,xM,yM,zM,cM);
13066       return CImg<Tdouble>(1,12).fill((Tdouble)m,(Tdouble)M,mean_value,variance_value,
13067                                       (Tdouble)xm,(Tdouble)ym,(Tdouble)zm,(Tdouble)cm,
13068                                       (Tdouble)xM,(Tdouble)yM,(Tdouble)zM,(Tdouble)cM);
13069     }
13070 
13071     //@}
13072     //-------------------------------------
13073     //
13074     //! \name Vector / Matrix Operations
13075     //@{
13076     //-------------------------------------
13077 
13078     //! Return the norm of the current vector/matrix. \p ntype = norm type (0=L2, 1=L1, -1=Linf).
13079     Tdouble magnitude(const int magnitude_type=2) const {
13080       if (is_empty())
13081         throw CImgInstanceException(_cimg_instance
13082                                     "magnitude() : Empty instance.",
13083                                     cimg_instance);
13084       Tdouble res = 0;
13085       switch (magnitude_type) {
13086       case -1 : {
13087         cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)cimg::abs(*ptrs); if (val>res) res = val; }
13088       } break;
13089       case 1 : {
13090         cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::abs(*ptrs);
13091       } break;
13092       default : {
13093         cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::sqr(*ptrs);
13094         res = (Tdouble)std::sqrt(res);
13095       }
13096       }
13097       return res;
13098     }
13099 
13100     //! Return the trace of the image, viewed as a matrix.
13101     Tdouble trace() const {
13102       if (is_empty())
13103         throw CImgInstanceException(_cimg_instance
13104                                     "trace() : Empty instance.",
13105                                     cimg_instance);
13106       Tdouble res = 0;
13107       cimg_forX(*this,k) res+=(Tdouble)(*this)(k,k);
13108       return res;
13109     }
13110 
13111     //! Return the determinant of the image, viewed as a matrix.
13112     Tdouble det() const {
13113       if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
13114         throw CImgInstanceException(_cimg_instance
13115                                     "det() : Instance is empty or not a square matrix.",
13116                                     cimg_instance);
13117 
13118       switch (_width) {
13119       case 1 : return (Tdouble)((*this)(0,0));
13120       case 2 : return (Tdouble)((*this)(0,0))*(Tdouble)((*this)(1,1)) - (Tdouble)((*this)(0,1))*(Tdouble)((*this)(1,0));
13121       case 3 : {
13122         const Tdouble
13123           a = (Tdouble)_data[0], d = (Tdouble)_data[1], g = (Tdouble)_data[2],
13124           b = (Tdouble)_data[3], e = (Tdouble)_data[4], h = (Tdouble)_data[5],
13125           c = (Tdouble)_data[6], f = (Tdouble)_data[7], i = (Tdouble)_data[8];
13126         return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
13127       }
13128       default : {
13129         CImg<Tfloat> lu(*this);
13130         CImg<uintT> indx;
13131         bool d;
13132         lu._LU(indx,d);
13133         Tdouble res = d?(Tdouble)1:(Tdouble)-1;
13134         cimg_forX(lu,i) res*=lu(i,i);
13135         return res;
13136       }
13137       }
13138       return 0;
13139     }
13140 
13141     //! Return the dot product of the current vector/matrix with the vector/matrix \p img.
13142     template<typename t>
13143     Tdouble dot(const CImg<t>& img) const {
13144       if (is_empty())
13145         throw CImgInstanceException(_cimg_instance
13146                                     "dot() : Empty instance.",
13147                                     cimg_instance);
13148       if (!img)
13149         throw CImgArgumentException(_cimg_instance
13150                                     "dot() : Empty specified image.",
13151                                     cimg_instance);
13152 
13153       const unsigned int nb = cimg::min(size(),img.size());
13154       Tdouble res = 0;
13155       for (unsigned int off = 0; off<nb; ++off) res+=(Tdouble)_data[off]*(Tdouble)img[off];
13156       return res;
13157     }
13158 
13159     //! Return a new image corresponding to the vector located at (\p x,\p y,\p z) of the current vector-valued image.
13160     CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
13161       static CImg<T> res;
13162       if (res._height!=_spectrum) res.assign(1,_spectrum);
13163       const unsigned int whd = _width*_height*_depth;
13164       const T *ptrs = data(x,y,z);
13165       T *ptrd = res._data;
13166       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
13167       return res;
13168     }
13169 
13170     //! Return a new image corresponding to the \a square \a matrix located at (\p x,\p y,\p z) of the current vector-valued image.
13171     CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
13172       const int n = (int)std::sqrt((double)_spectrum);
13173       const T *ptrs = data(x,y,z,0);
13174       const unsigned int whd = _width*_height*_depth;
13175       CImg<T> res(n,n);
13176       T *ptrd = res._data;
13177       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
13178       return res;
13179     }
13180 
13181     //! Return a new image corresponding to the \a diffusion \a tensor located at (\p x,\p y,\p z) of the current vector-valued image.
13182     CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
13183       const T *ptrs = data(x,y,z,0);
13184       const unsigned int whd = _width*_height*_depth;
13185       if (_spectrum==6) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd),*(ptrs+3*whd),*(ptrs+4*whd),*(ptrs+5*whd));
13186       if (_spectrum==3) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd));
13187       return tensor(*ptrs);
13188     }
13189 
13190     //! Set the image \p vec as the \a vector \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image.
13191     template<typename t>
13192     CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
13193       if (x<_width && y<_height && z<_depth) {
13194         const t *ptrs = vec._data;
13195         const unsigned int whd = _width*_height*_depth;
13196         T *ptrd = data(x,y,z);
13197         for (unsigned int k = cimg::min((unsigned int)vec.size(),_spectrum); k; --k) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
13198       }
13199       return *this;
13200     }
13201 
13202     //! Set the image \p vec as the \a square \a matrix-valued pixel located at (\p x,\p y,\p z) of the current vector-valued image.
13203     template<typename t>
13204     CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
13205       return set_vector_at(mat,x,y,z);
13206     }
13207 
13208     //! Set the image \p vec as the \a tensor \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image.
13209     template<typename t>
13210     CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
13211       T *ptrd = data(x,y,z,0);
13212       const unsigned int siz = _width*_height*_depth;
13213       if (ten._height==2) {
13214         *ptrd = (T)ten[0]; ptrd+=siz;
13215         *ptrd = (T)ten[1]; ptrd+=siz;
13216         *ptrd = (T)ten[3];
13217       }
13218       else {
13219         *ptrd = (T)ten[0]; ptrd+=siz;
13220         *ptrd = (T)ten[1]; ptrd+=siz;
13221         *ptrd = (T)ten[2]; ptrd+=siz;
13222         *ptrd = (T)ten[4]; ptrd+=siz;
13223         *ptrd = (T)ten[5]; ptrd+=siz;
13224         *ptrd = (T)ten[8];
13225       }
13226       return *this;
13227     }
13228 
13229     //! Unroll all images values into a one-column vector.
13230     CImg<T>& vector() {
13231       return unroll('y');
13232     }
13233 
13234     CImg<T> get_vector() const {
13235       return get_unroll('y');
13236     }
13237 
13238     //! Realign pixel values of the instance image as a square matrix
13239     CImg<T>& matrix() {
13240       const unsigned int siz = size();
13241       switch (siz) {
13242       case 1 : break;
13243       case 4 : _width = _height = 2; break;
13244       case 9 : _width = _height = 3; break;
13245       case 16 : _width = _height = 4; break;
13246       case 25 : _width = _height = 5; break;
13247       case 36 : _width = _height = 6; break;
13248       case 49 : _width = _height = 7; break;
13249       case 64 : _width = _height = 8; break;
13250       case 81 : _width = _height = 9; break;
13251       case 100 : _width = _height = 10; break;
13252       default : {
13253         unsigned int i = 11, i2 = i*i;
13254         while (i2<siz) { i2+=2*i + 1; ++i; }
13255         if (i2==siz) _width = _height = i;
13256         else throw CImgInstanceException(_cimg_instance
13257                                          "matrix() : Invalid instance size %u (should be a square integer).",
13258                                          cimg_instance,
13259                                          siz);
13260       }
13261       }
13262       return *this;
13263     }
13264 
13265     CImg<T> get_matrix() const {
13266       return (+*this).matrix();
13267     }
13268 
13269     //! Realign pixel values of the instance image as a symmetric tensor.
13270     CImg<T>& tensor() {
13271       return get_tensor().move_to(*this);
13272     }
13273 
13274     CImg<T> get_tensor() const {
13275       CImg<T> res;
13276       const unsigned int siz = size();
13277       switch (siz) {
13278       case 1 : break;
13279       case 3 :
13280         res.assign(2,2);
13281         res(0,0) = (*this)(0);
13282         res(1,0) = res(0,1) = (*this)(1);
13283         res(1,1) = (*this)(2);
13284         break;
13285       case 6 :
13286         res.assign(3,3);
13287         res(0,0) = (*this)(0);
13288         res(1,0) = res(0,1) = (*this)(1);
13289         res(2,0) = res(0,2) = (*this)(2);
13290         res(1,1) = (*this)(3);
13291         res(2,1) = res(1,2) = (*this)(4);
13292         res(2,2) = (*this)(5);
13293         break;
13294       default :
13295         throw CImgInstanceException(_cimg_instance
13296                                     "tensor() : Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).",
13297                                     cimg_instance);
13298       }
13299       return res;
13300     }
13301 
13302     //! Get a diagonal matrix, whose diagonal coefficients are the coefficients of the input image.
13303     CImg<T>& diagonal() {
13304       return get_diagonal().move_to(*this);
13305     }
13306 
13307     CImg<T> get_diagonal() const {
13308       if (is_empty()) return *this;
13309       CImg<T> res(size(),size(),1,1,0);
13310       cimg_foroff(*this,off) res(off,off) = (*this)(off);
13311       return res;
13312     }
13313 
13314     //! Get an identity matrix having same dimension than instance image.
13315     CImg<T>& identity_matrix() {
13316       return identity_matrix(cimg::max(_width,_height)).move_to(*this);
13317     }
13318 
13319     CImg<T> get_identity_matrix() const {
13320       return identity_matrix(cimg::max(_width,_height));
13321     }
13322 
13323     //! Return a N-numbered sequence vector from \p a0 to \p a1.
13324     CImg<T>& sequence(const T a0, const T a1) {
13325       if (is_empty()) return *this;
13326       const unsigned int siz = size() - 1;
13327       T* ptr = _data;
13328       if (siz) {
13329         const Tdouble delta = (Tdouble)a1 - (Tdouble)a0;
13330         cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
13331       } else *ptr = a0;
13332       return *this;
13333     }
13334 
13335     CImg<T> get_sequence(const T a0, const T a1) const {
13336       return (+*this).sequence(a0,a1);
13337     }
13338 
13339     //! Transpose the current matrix.
13340     CImg<T>& transpose() {
13341       if (_width==1) { _width = _height; _height = 1; return *this; }
13342       if (_height==1) { _height = _width; _width = 1; return *this; }
13343       if (_width==_height) {
13344         cimg_forYZC(*this,y,z,c) for (int x = y; x<width(); ++x) cimg::swap((*this)(x,y,z,c),(*this)(y,x,z,c));
13345         return *this;
13346       }
13347       return get_transpose().move_to(*this);
13348     }
13349 
13350     CImg<T> get_transpose() const {
13351       return get_permute_axes("yxzc");
13352     }
13353 
13354     //! Compute the cross product between two 3d vectors.
13355     template<typename t>
13356     CImg<T>& cross(const CImg<t>& img) {
13357       if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
13358         throw CImgInstanceException(_cimg_instance
13359                                     "cross() : Instance and/or specified image (%u,%u,%u,%u,%p) are not 3d vectors.",
13360                                     cimg_instance,
13361                                     img._width,img._height,img._depth,img._spectrum,img._data);
13362 
13363       const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
13364       (*this)[0] = (T)(y*img[2] - z*img[1]);
13365       (*this)[1] = (T)(z*img[0] - x*img[2]);
13366       (*this)[2] = (T)(x*img[1] - y*img[0]);
13367       return *this;
13368     }
13369 
13370     template<typename t>
13371     CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
13372       return CImg<_cimg_Tt>(*this).cross(img);
13373     }
13374 
13375     //! Invert the current matrix.
13376     CImg<T>& invert(const bool use_LU=true) {
13377       if (_width!=_height || _depth!=1 || _spectrum!=1)
13378         throw CImgInstanceException(_cimg_instance
13379                                     "invert() : Instance is not a square matrix.",
13380                                     cimg_instance);
13381 #ifdef cimg_use_lapack
13382       int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
13383       Tfloat
13384         *const lapA = new Tfloat[N*N],
13385         *const WORK = new Tfloat[LWORK];
13386       cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
13387       cimg::getrf(N,lapA,IPIV,INFO);
13388       if (INFO)
13389         cimg::warn(_cimg_instance
13390                    "invert() : LAPACK function dgetrf_() returned error code %d.",
13391                    cimg_instance,
13392                    INFO);
13393       else {
13394         cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
13395         if (INFO)
13396           cimg::warn(_cimg_instance
13397                      "invert() : LAPACK function dgetri_() returned error code %d.",
13398                      cimg_instance,
13399                      INFO);
13400       }
13401       if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N+l]); else fill(0);
13402       delete[] IPIV; delete[] lapA; delete[] WORK;
13403 #else
13404       const double dete = _width>3?-1.0:det();
13405       if (dete!=0.0 && _width==2) {
13406         const double
13407           a = _data[0], c = _data[1],
13408           b = _data[2], d = _data[3];
13409         _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
13410         _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
13411       } else if (dete!=0.0 && _width==3) {
13412         const double
13413           a = _data[0], d = _data[1], g = _data[2],
13414           b = _data[3], e = _data[4], h = _data[5],
13415           c = _data[6], f = _data[7], i = _data[8];
13416         _data[0] = (T)((i*e-f*h)/dete), _data[1] = (T)((g*f-i*d)/dete), _data[2] = (T)((d*h-g*e)/dete);
13417         _data[3] = (T)((h*c-i*b)/dete), _data[4] = (T)((i*a-c*g)/dete), _data[5] = (T)((g*b-a*h)/dete);
13418         _data[6] = (T)((b*f-e*c)/dete), _data[7] = (T)((d*c-a*f)/dete), _data[8] = (T)((a*e-d*b)/dete);
13419       } else {
13420         if (use_LU) { // LU-based inverse computation
13421           CImg<Tfloat> A(*this), indx, col(1,_width);
13422           bool d;
13423           A._LU(indx,d);
13424           cimg_forX(*this,j) {
13425             col.fill(0);
13426             col(j) = 1;
13427             col._solve(A,indx);
13428             cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
13429           }
13430         } else { // SVD-based inverse computation
13431           CImg<Tfloat> U(_width,_width), S(1,_width), V(_width,_width);
13432           SVD(U,S,V,false);
13433           U.transpose();
13434           cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k];
13435           S.diagonal();
13436           *this = V*S*U;
13437         }
13438       }
13439 #endif
13440       return *this;
13441     }
13442 
13443     CImg<Tfloat> get_invert(const bool use_LU=true) const {
13444       return CImg<Tfloat>(*this,false).invert(use_LU);
13445     }
13446 
13447     //! Compute the pseudo-inverse (Moore-Penrose) of the matrix.
13448     CImg<T>& pseudoinvert() {
13449       return get_pseudoinvert().move_to(*this);
13450     }
13451 
13452     CImg<Tfloat> get_pseudoinvert() const {
13453       CImg<Tfloat> U, S, V;
13454       SVD(U,S,V);
13455       cimg_forX(V,x) {
13456         const Tfloat s = S(x), invs = s!=0?1/s:(Tfloat)0;
13457         cimg_forY(V,y) V(x,y)*=invs;
13458       }
13459       return V*U.transpose();
13460     }
13461 
13462     //! Solve a linear system AX=B where B=*this.
13463     template<typename t>
13464     CImg<T>& solve(const CImg<t>& A) {
13465       if (_width!=1 || _depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
13466         throw CImgArgumentException(_cimg_instance
13467                                     "solve() : Instance and specified matrix (%u,%u,%u,%u,%p) have incompatible dimensions.",
13468                                     cimg_instance,
13469                                     A._width,A._height,A._depth,A._spectrum,A._data);
13470 
13471       typedef _cimg_Ttfloat Ttfloat;
13472       if (A._width==A._height) {
13473 #ifdef cimg_use_lapack
13474         char TRANS='N';
13475         int INFO, N = _height, LWORK = 4*N, one = 1, *const IPIV = new int[N];
13476         Ttfloat
13477           *const lapA = new Ttfloat[N*N],
13478           *const lapB = new Ttfloat[N],
13479           *const WORK = new Ttfloat[LWORK];
13480         cimg_forXY(A,k,l) lapA[k*N+l] = (Ttfloat)(A(k,l));
13481         cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i));
13482         cimg::getrf(N,lapA,IPIV,INFO);
13483         if (INFO)
13484           cimg::warn(_cimg_instance
13485                      "solve() : LAPACK library function dgetrf_() returned error code %d.",
13486                      cimg_instance,
13487                      INFO);
13488 
13489         if (!INFO) {
13490           cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
13491           if (INFO)
13492             cimg::warn(_cimg_instance
13493                        "solve() : LAPACK library function dgetrs_() returned error code %d.",
13494                        cimg_instance,
13495                        INFO);
13496         }
13497         if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0);
13498         delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
13499 #else
13500         CImg<Ttfloat> lu(A);
13501         CImg<Ttfloat> indx;
13502         bool d;
13503         lu._LU(indx,d);
13504         _solve(lu,indx);
13505 #endif
13506       } else assign(A.get_pseudoinvert()*(*this));
13507       return *this;
13508     }
13509 
13510     template<typename t>
13511     CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A) const {
13512       return CImg<_cimg_Ttfloat>(*this,false).solve(A);
13513     }
13514 
13515     template<typename t, typename ti>
13516     CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
13517       typedef _cimg_Ttfloat Ttfloat;
13518       const int N = size();
13519       int ii = -1;
13520       Ttfloat sum;
13521       for (int i = 0; i<N; ++i) {
13522         const int ip = (int)indx[i];
13523         Ttfloat sum = (*this)(ip);
13524         (*this)(ip) = (*this)(i);
13525         if (ii>=0) for (int j = ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j);
13526         else if (sum!=0) ii=i;
13527         (*this)(i) = (T)sum;
13528       }
13529       for (int i = N-1; i>=0; --i) {
13530         sum = (*this)(i);
13531         for (int j = i+1; j<N; ++j) sum-=A(j,i)*(*this)(j);
13532         (*this)(i) = (T)(sum/A(i,i));
13533       }
13534       return *this;
13535     }
13536 
13537     //! Solve a linear system AX=B where B=*this and A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ].
13538     // (Use the Thomas Algorithm).
13539     template<typename t>
13540     CImg<T>& solve_tridiagonal(const CImg<t>& a, const CImg<t>& b, const CImg<t>& c) {
13541       const int siz = (int)size();
13542       if ((int)a.size()!=siz || (int)b.size()!=siz || (int)c.size()!=siz)
13543         throw CImgArgumentException(_cimg_instance
13544                                     "solve_tridiagonal() : Instance and tridiagonal coefficients "
13545                                     "(%u,%u,%u,%u,%p), (%u,%u,%u,%u,%p) and (%u,%u,%u,%u,%p) have incompatible dimensions.",
13546                                     cimg_instance,
13547                                     a._width,a._height,a._depth,a._spectrum,a._data,
13548                                     b._width,b._height,b._depth,b._spectrum,b._data,
13549                                     c._width,c._height,c._depth,c._spectrum,c._data);
13550 
13551       typedef _cimg_Ttfloat Ttfloat;
13552       CImg<Ttfloat> nc(siz);
13553       const T *ptra = a._data, *ptrb = b._data, *ptrc = c._data;
13554       T *ptrnc = nc._data, *ptrd = _data;
13555       const Ttfloat valb0 = (Ttfloat)*(ptrb++);
13556       *ptrnc = *(ptrc++)/valb0;
13557       Ttfloat vald = (Ttfloat)(*(ptrd++)/=valb0);
13558       for (int i = 1; i<siz; ++i) {
13559         const Ttfloat
13560           vala = (Tfloat)*(ptra++),
13561           id = 1/(*(ptrb++) - *(ptrnc++)*vala);
13562         *ptrnc = *(ptrc++)*id;
13563         vald = ((*ptrd-=vala*vald)*=id);
13564         ++ptrd;
13565       }
13566       vald = *(--ptrd);
13567       for (int i = siz-2; i>=0; --i) vald = (*(--ptrd)-=*(--ptrnc)*vald);
13568       return *this;
13569     }
13570 
13571     template<typename t>
13572     CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& a, const CImg<t>& b, const CImg<t>& c) const {
13573       return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(a,b,c);
13574     }
13575 
13576     //! Compute the eigenvalues and eigenvectors of a matrix.
13577     template<typename t>
13578     const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
13579       if (is_empty()) { val.assign(); vec.assign(); }
13580       else {
13581         if (_width!=_height || _depth>1 || _spectrum>1)
13582           throw CImgInstanceException(_cimg_instance
13583                                       "eigen() : Instance is not a square matrix.",
13584                                       cimg_instance);
13585 
13586         if (val.size()<_width) val.assign(1,_width);
13587         if (vec.size()<_width*_width) vec.assign(_width,_width);
13588         switch (_width) {
13589         case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
13590         case 2 : {
13591           const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
13592           double f = e*e - 4*(a*d - b*c);
13593           if (f<0)
13594             cimg::warn(_cimg_instance
13595                        "CImg<%s>::eigen() : Complex eigenvalues found.",
13596                        cimg_instance);
13597 
13598           f = std::sqrt(f);
13599           const double l1 = 0.5*(e-f), l2 = 0.5*(e+f);
13600           const double theta1 = std::atan2(l2-a,b), theta2 = std::atan2(l1-a,b);
13601           val[0]=(t)l2;
13602           val[1]=(t)l1;
13603           vec(0,0) = (t)std::cos(theta1);
13604           vec(0,1) = (t)std::sin(theta1);
13605           vec(1,0) = (t)std::cos(theta2);
13606           vec(1,1) = (t)std::sin(theta2);
13607         } break;
13608         default :
13609           throw CImgInstanceException(_cimg_instance
13610                                       "eigen() : Eigenvalues computation of general matrices is limited to 2x2 matrices.",
13611                                       cimg_instance);
13612         }
13613       }
13614       return *this;
13615     }
13616 
13617     CImgList<Tfloat> get_eigen() const {
13618       CImgList<Tfloat> res(2);
13619       eigen(res[0],res[1]);
13620       return res;
13621     }
13622 
13623     //! Compute the eigenvalues and eigenvectors of a symmetric matrix.
13624     template<typename t>
13625     const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
13626       if (is_empty()) { val.assign(); vec.assign(); }
13627       else {
13628 #ifdef cimg_use_lapack
13629         char JOB = 'V', UPLO = 'U';
13630         int N = _width, LWORK = 4*N, INFO;
13631         Tfloat
13632           *const lapA = new Tfloat[N*N],
13633           *const lapW = new Tfloat[N],
13634           *const WORK = new Tfloat[LWORK];
13635         cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
13636         cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
13637         if (INFO)
13638           cimg::warn(_cimg_instance
13639                      "symmetric_eigen() : LAPACK library function dsyev_() returned error code %d.",
13640                      cimg_instance,
13641                      INFO);
13642 
13643         val.assign(1,N);
13644         vec.assign(N,N);
13645         if (!INFO) {
13646           cimg_forY(val,i) val(i) = (T)lapW[N-1-i];
13647           cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N-1-k)*N+l]);
13648         } else { val.fill(0); vec.fill(0); }
13649         delete[] lapA; delete[] lapW; delete[] WORK;
13650 #else
13651         if (_width!=_height || _depth>1 || _spectrum>1)
13652           throw CImgInstanceException(_cimg_instance
13653                                       "eigen() : Instance is not a square matrix.",
13654                                       cimg_instance);
13655 
13656         val.assign(1,_width);
13657         if (vec._data) vec.assign(_width,_width);
13658         if (_width<3) return eigen(val,vec);
13659         CImg<t> V(_width,_width);
13660         SVD(vec,val,V,false);
13661         bool ambiguous = false;
13662         float eig = 0;
13663         cimg_forY(val,p) {       // check for ambiguous cases.
13664           if (val[p]>eig) eig = (float)val[p];
13665           t scal = 0;
13666           cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
13667           if (cimg::abs(scal)<0.9f) ambiguous = true;
13668           if (scal<0) val[p] = -val[p];
13669         }
13670         if (ambiguous) {
13671           ++(eig*=2);
13672           SVD(vec,val,V,false,40,eig);
13673           val-=eig;
13674         }
13675         CImg<intT> permutations(_width);  // sort eigenvalues in decreasing order
13676         CImg<t> tmp(_width);
13677         val.sort(permutations,false);
13678         cimg_forY(vec,k) {
13679           cimg_forX(permutations,x) tmp(x) = vec(permutations(x),k);
13680           std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
13681         }
13682 #endif
13683       }
13684       return *this;
13685     }
13686 
13687     CImgList<Tfloat> get_symmetric_eigen() const {
13688       CImgList<Tfloat> res(2);
13689       symmetric_eigen(res[0],res[1]);
13690       return res;
13691     }
13692 
13693     //! Sort values of a vector and get permutations.
13694     template<typename t>
13695     CImg<T>& sort(CImg<t>& permutations, const bool increasing=true) {
13696       if (is_empty()) permutations.assign();
13697       else {
13698         if (permutations.size()!=size()) permutations.assign(size());
13699         cimg_foroff(permutations,off) permutations[off] = (t)off;
13700         _quicksort(0,size()-1,permutations,increasing);
13701       }
13702       return *this;
13703     }
13704 
13705     template<typename t>
13706     CImg<T> get_sort(CImg<t>& permutations, const bool increasing=true) const {
13707       return (+*this).sort(permutations,increasing);
13708     }
13709 
13710     //! Sort image values.
13711     CImg<T>& sort(const bool increasing=true) {
13712       CImg<T> foo;
13713       return sort(foo,increasing);
13714     }
13715 
13716     CImg<T> get_sort(const bool increasing=true) const {
13717       return (+*this).sort(increasing);
13718     }
13719 
13720     template<typename t>
13721     CImg<T>& _quicksort(const int min, const int max, CImg<t>& permutations, const bool increasing) {
13722       if (min<max) {
13723         const int mid = (min+max)/2;
13724         if (increasing) {
13725           if ((*this)[min]>(*this)[mid]) {
13726             cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); }
13727           if ((*this)[mid]>(*this)[max]) {
13728             cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); }
13729           if ((*this)[min]>(*this)[mid]) {
13730             cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); }
13731         } else {
13732           if ((*this)[min]<(*this)[mid]) {
13733             cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); }
13734           if ((*this)[mid]<(*this)[max]) {
13735             cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); }
13736           if ((*this)[min]<(*this)[mid]) {
13737             cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); }
13738         }
13739         if (max-min>=3) {
13740           const T pivot = (*this)[mid];
13741           int i = min, j = max;
13742           if (increasing) {
13743             do {
13744               while ((*this)[i]<pivot) ++i;
13745               while ((*this)[j]>pivot) --j;
13746               if (i<=j) {
13747                 cimg::swap((*this)[i],(*this)[j]);
13748                 cimg::swap(permutations[i++],permutations[j--]);
13749               }
13750             } while (i<=j);
13751           } else {
13752             do {
13753               while ((*this)[i]>pivot) ++i;
13754               while ((*this)[j]<pivot) --j;
13755               if (i<=j) {
13756                 cimg::swap((*this)[i],(*this)[j]);
13757                 cimg::swap(permutations[i++],permutations[j--]);
13758               }
13759             } while (i<=j);
13760           }
13761           if (min<j) _quicksort(min,j,permutations,increasing);
13762           if (i<max) _quicksort(i,max,permutations,increasing);
13763         }
13764       }
13765       return *this;
13766     }
13767 
13768     //! Compute the SVD of a general matrix.
13769     template<typename t>
13770     const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
13771                        const unsigned int max_iteration=40, const float lambda=0) const {
13772       if (is_empty()) { U.assign(); S.assign(); V.assign(); }
13773       else {
13774         U = *this;
13775         if (lambda!=0) {
13776           const unsigned int delta = cimg::min(U._width,U._height);
13777           for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
13778         }
13779         if (S.size()<_width) S.assign(1,_width);
13780         if (V._width<_width || V._height<_height) V.assign(_width,_width);
13781         CImg<t> rv1(_width);
13782         t anorm = 0, c, f, g = 0, h, s, scale = 0;
13783         int l = 0, nm = 0;
13784 
13785         cimg_forX(U,i) {
13786           l = i+1; rv1[i] = scale*g; g = s = scale = 0;
13787           if (i<height()) {
13788             for (int k = i; k<height(); ++k) scale+= cimg::abs(U(i,k));
13789             if (scale) {
13790               for (int k = i; k<height(); ++k) { U(i,k)/=scale; s+= U(i,k)*U(i,k); }
13791               f = U(i,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g;
13792               for (int j = l; j<width(); ++j) {
13793                 s = 0;
13794                 for (int k=i; k<height(); ++k) s+= U(i,k)*U(j,k);
13795                 f = s/h;
13796                 for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
13797               }
13798               for (int k = i; k<height(); ++k) U(i,k)*= scale;
13799             }
13800           }
13801           S[i]=scale*g;
13802 
13803           g = s = scale = 0;
13804           if (i<height() && i!=width()-1) {
13805             for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
13806             if (scale) {
13807               for (int k = l; k<width(); ++k) { U(k,i)/= scale; s+= U(k,i)*U(k,i); }
13808               f = U(l,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g;
13809               for (int k = l; k<width(); ++k) rv1[k]=U(k,i)/h;
13810               for (int j = l; j<height(); ++j) {
13811                 s = 0;
13812                 for (int k = l; k<width(); ++k) s+= U(k,j)*U(k,i);
13813                 for (int k = l; k<width(); ++k) U(k,j)+= s*rv1[k];
13814               }
13815               for (int k = l; k<width(); ++k) U(k,i)*= scale;
13816             }
13817           }
13818           anorm = (t)cimg::max((float)anorm,(float)(cimg::abs(S[i])+cimg::abs(rv1[i])));
13819         }
13820 
13821         for (int i = width()-1; i>=0; --i) {
13822           if (i<width()-1) {
13823             if (g) {
13824               for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
13825               for (int j = l; j<width(); ++j) {
13826                 s = 0;
13827                 for (int k = l; k<width(); ++k) s+= U(k,i)*V(j,k);
13828                 for (int k = l; k<width(); ++k) V(j,k)+= s*V(i,k);
13829               }
13830             }
13831             for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.0;
13832           }
13833           V(i,i) = (t)1.0; g = rv1[i]; l = i;
13834         }
13835 
13836         for (int i = cimg::min(width(),height())-1; i>=0; --i) {
13837           l = i+1; g = S[i];
13838           for (int j = l; j<width(); ++j) U(j,i) = 0;
13839           if (g) {
13840             g = 1/g;
13841             for (int j = l; j<width(); ++j) {
13842               s = 0; for (int k = l; k<height(); ++k) s+= U(i,k)*U(j,k);
13843               f = (s/U(i,i))*g;
13844               for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
13845             }
13846             for (int j = i; j<height(); ++j) U(i,j)*= g;
13847           } else for (int j = i; j<height(); ++j) U(i,j) = 0;
13848           ++U(i,i);
13849         }
13850 
13851         for (int k = width()-1; k>=0; --k) {
13852           for (unsigned int its = 0; its<max_iteration; ++its) {
13853             bool flag = true;
13854             for (l = k; l>=1; --l) {
13855               nm = l-1;
13856               if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; }
13857               if ((cimg::abs(S[nm])+anorm)==anorm) break;
13858             }
13859             if (flag) {
13860               c = 0; s = 1;
13861               for (int i = l; i<=k; ++i) {
13862                 f = s*rv1[i]; rv1[i] = c*rv1[i];
13863                 if ((cimg::abs(f)+anorm)==anorm) break;
13864                 g = S[i]; h = (t)cimg::_pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h;
13865                 cimg_forY(U,j) { const t y = U(nm,j), z = U(i,j); U(nm,j) = y*c+z*s; U(i,j) = z*c-y*s; }
13866               }
13867             }
13868             const t z = S[k];
13869             if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
13870             nm = k-1;
13871             t x = S[l], y = S[nm];
13872             g = rv1[nm]; h = rv1[k];
13873             f = ((y-z)*(y+z)+(g-h)*(g+h))/(2*h*y);
13874             g = (t)cimg::_pythagore(f,1.0);
13875             f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x;
13876             c = s = 1;
13877             for (int j = l; j<=nm; ++j) {
13878               const int i = j+1;
13879               g = rv1[i]; h = s*g; g = c*g;
13880               t y = S[i];
13881               t z = (t)cimg::_pythagore(f,h);
13882               rv1[j] = z; c = f/z; s = h/z;
13883               f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c;
13884               cimg_forX(U,jj) { const t x = V(j,jj), z = V(i,jj); V(j,jj) = x*c+z*s; V(i,jj) = z*c-x*s; }
13885               z = (t)cimg::_pythagore(f,h); S[j] = z;
13886               if (z) { z = 1/z; c = f*z; s = h*z; }
13887               f = c*g+s*y; x = c*y-s*g;
13888               cimg_forY(U,jj) { const t y = U(j,jj); z = U(i,jj); U(j,jj) = y*c+z*s; U(i,jj) = z*c-y*s; }
13889             }
13890             rv1[l] = 0; rv1[k]=f; S[k]=x;
13891           }
13892         }
13893 
13894         if (sorting) {
13895           CImg<intT> permutations(_width);
13896           CImg<t> tmp(_width);
13897           S.sort(permutations,false);
13898           cimg_forY(U,k) {
13899             cimg_forX(permutations,x) tmp(x) = U(permutations(x),k);
13900             std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
13901           }
13902           cimg_forY(V,k) {
13903             cimg_forX(permutations,x) tmp(x) = V(permutations(x),k);
13904             std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
13905           }
13906         }
13907       }
13908       return *this;
13909     }
13910 
13911     CImgList<Tfloat> get_SVD(const bool sorting=true,
13912                              const unsigned int max_iteration=40, const float lambda=0) const {
13913       CImgList<Tfloat> res(3);
13914       SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
13915       return res;
13916     }
13917 
13918     // INNER ROUTINE : Compute the LU decomposition of a permuted matrix (c.f. numerical recipies)
13919     template<typename t>
13920     CImg<T>& _LU(CImg<t>& indx, bool& d) {
13921       const int N = width();
13922       int imax = 0;
13923       CImg<Tfloat> vv(N);
13924       indx.assign(N);
13925       d = true;
13926       cimg_forX(*this,i) {
13927         Tfloat vmax = 0;
13928         cimg_forX(*this,j) {
13929           const Tfloat tmp = cimg::abs((*this)(j,i));
13930           if (tmp>vmax) vmax = tmp;
13931         }
13932         if (vmax==0) { indx.fill(0); return fill(0); }
13933         vv[i] = 1/vmax;
13934       }
13935       cimg_forX(*this,j) {
13936         for (int i = 0; i<j; ++i) {
13937           Tfloat sum=(*this)(j,i);
13938           for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
13939           (*this)(j,i) = (T)sum;
13940         }
13941         Tfloat vmax = 0;
13942         for (int i = j; i<width(); ++i) {
13943           Tfloat sum=(*this)(j,i);
13944           for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
13945           (*this)(j,i) = (T)sum;
13946           const Tfloat tmp = vv[i]*cimg::abs(sum);
13947           if (tmp>=vmax) { vmax=tmp; imax=i; }
13948         }
13949         if (j!=imax) {
13950           cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
13951           d =!d;
13952           vv[imax] = vv[j];
13953         }
13954         indx[j] = (t)imax;
13955         if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
13956         if (j<N) {
13957           const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
13958           for (int i=j+1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
13959         }
13960       }
13961       return *this;
13962     }
13963 
13964     //! Compute minimal path in a graph, using the Dijkstra algorithm.
13965     /**
13966        \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance between two nodes (i,j).
13967        \param nb_nodes Number of graph nodes.
13968        \param starting_node Indice of the starting node.
13969        \param ending_node Indice of the ending node (set to ~0U to ignore ending node).
13970        \param previous Array that gives the previous node indice in the path to the starting node (optional parameter).
13971        \return Array of distances of each node to the starting node.
13972     **/
13973     template<typename tf, typename t>
13974     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
13975                             const unsigned int starting_node, const unsigned int ending_node,
13976                             CImg<t>& previous) {
13977 
13978       CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
13979       dist(starting_node) = 0;
13980       previous.assign(1,nb_nodes,1,1,(t)-1);
13981       previous(starting_node) = (t)starting_node;
13982       CImg<uintT> Q(nb_nodes);
13983       cimg_forX(Q,u) Q(u) = u;
13984       cimg::swap(Q(starting_node),Q(0));
13985       unsigned int sizeQ = nb_nodes;
13986       while (sizeQ) {
13987         // Update neighbors from minimal vertex
13988         const unsigned int umin = Q(0);
13989         if (umin==ending_node) sizeQ = 0;
13990         else {
13991           const T dmin = dist(umin);
13992           const T infty = cimg::type<T>::max();
13993           for (unsigned int q = 1; q<sizeQ; ++q) {
13994             const unsigned int v = Q(q);
13995             const T d = (T)distance(v,umin);
13996             if (d<infty) {
13997               const T alt = dmin + d;
13998               if (alt<dist(v)) {
13999                 dist(v) = alt;
14000                 previous(v) = (t)umin;
14001                 const T distpos = dist(Q(q));
14002                 for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos+1)/2-1)); pos=par) cimg::swap(Q(pos),Q(par));
14003               }
14004             }
14005           }
14006           // Remove minimal vertex from queue
14007           Q(0) = Q(--sizeQ);
14008           const T distpos = dist(Q(0));
14009           for (unsigned int pos = 0, left = 0, right = 0;
14010                ((right=2*(pos+1),(left=right-1))<sizeQ && distpos>dist(Q(left))) || (right<sizeQ && distpos>dist(Q(right)));) {
14011             if (right<sizeQ) {
14012               if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
14013               else { cimg::swap(Q(pos),Q(right)); pos = right; }
14014             } else { cimg::swap(Q(pos),Q(left)); pos = left; }
14015           }
14016         }
14017       }
14018       return dist;
14019     }
14020 
14021     //! Return minimal path in a graph, using the Dijkstra algorithm.
14022     template<typename tf, typename t>
14023     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
14024                             const unsigned int starting_node, const unsigned int ending_node=~0U) {
14025       CImg<uintT> foo;
14026       return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
14027     }
14028 
14029     //! Return minimal path in a graph, using the Dijkstra algorithm.
14030     /**
14031        Instance image corresponds to the adjacency matrix of the graph.
14032        \param starting_node Indice of the starting node.
14033        \param previous Array that gives the previous node indice in the path to the starting node (optional parameter).
14034        \return Array of distances of each node to the starting node.
14035     **/
14036     template<typename t>
14037     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& previous) {
14038       return get_dijkstra(starting_node,ending_node,previous).move_to(*this);
14039     }
14040 
14041     template<typename t>
14042     CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& previous) const {
14043       if (_width!=_height || _depth!=1 || _spectrum!=1)
14044         throw CImgInstanceException(_cimg_instance
14045                                     "dijkstra() : Instance is not a graph adjacency matrix.",
14046                                     cimg_instance);
14047 
14048       return dijkstra(*this,_width,starting_node,ending_node,previous);
14049     }
14050 
14051     //! Return minimal path in a graph, using the Dijkstra algorithm.
14052     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
14053       return get_dijkstra(starting_node,ending_node).move_to(*this);
14054     }
14055 
14056     CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
14057       CImg<uintT> foo;
14058       return get_dijkstra(starting_node,ending_node,foo);
14059     }
14060 
14061     //! Return stream line of a 2d or 3d vector field.
14062     CImg<floatT> get_streamline(const float x, const float y, const float z,
14063                                 const float L=256, const float dl=0.1f,
14064                                 const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
14065                                 const bool is_oriented_only=false) const {
14066       if (_spectrum!=2 && _spectrum!=3)
14067         throw CImgInstanceException(_cimg_instance
14068                                     "streamline() : Instance is not a 2d or 3d vector field.",
14069                                     cimg_instance);
14070       if (_spectrum==2) {
14071         if (is_oriented_only) {
14072           typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
14073           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,0.0f);
14074         } else {
14075           typename CImg<T>::_functor4d_streamline2d_directed func(*this);
14076           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,0.0f);
14077         }
14078       }
14079       if (is_oriented_only) {
14080         typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
14081         return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f);
14082       }
14083       typename CImg<T>::_functor4d_streamline3d_directed func(*this);
14084       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f);
14085     }
14086 
14087     //! Return stream line of a 3d vector field.
14088     /**
14089        \param interpolation_type Type of interpolation (can be 0=nearest int, 1=linear, 2=2nd-order RK, 3=4th-order RK.
14090 
14091      **/
14092     template<typename tfunc>
14093     static CImg<floatT> streamline(const tfunc& func,
14094                                    const float x, const float y, const float z,
14095                                    const float L=256, const float dl=0.1f,
14096                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
14097                                    const bool is_oriented_only=false,
14098                                    const float x0=0, const float y0=0, const float z0=0,
14099                                    const float x1=0, const float y1=0, const float z1=0) {
14100       if (dl<=0)
14101         throw CImgArgumentException("CImg<%s>::streamline() : Invalid specified integration length %g "
14102                                     "(should be >0).",
14103                                     pixel_type(),
14104                                     dl);
14105 
14106       const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
14107       if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
14108       const unsigned int size_L = (unsigned int)cimg::round(L/dl+1,1);
14109       CImg<floatT> coordinates(size_L,3);
14110       const float dl2 = dl/2;
14111       float
14112         *ptr_x = coordinates.data(0,0),
14113         *ptr_y = coordinates.data(0,1),
14114         *ptr_z = coordinates.data(0,2),
14115         pu = (float)(dl*func(x,y,z,0)),
14116         pv = (float)(dl*func(x,y,z,1)),
14117         pw = (float)(dl*func(x,y,z,2)),
14118         X = x, Y = y, Z = z;
14119 
14120       switch (interpolation_type) {
14121       case 0 : { // Nearest integer interpolation.
14122         cimg_forX(coordinates,l) {
14123           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
14124           const int
14125             xi = (int)(X>0?X+0.5f:X-0.5f),
14126             yi = (int)(Y>0?Y+0.5f:Y-0.5f),
14127             zi = (int)(Z>0?Z+0.5f:Z-0.5f);
14128           float
14129             u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
14130             v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
14131             w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
14132           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
14133           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
14134           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
14135         }
14136       } break;
14137       case 1 : { // First-order interpolation.
14138         cimg_forX(coordinates,l) {
14139           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
14140           float
14141             u = (float)(dl*func(X,Y,Z,0)),
14142             v = (float)(dl*func(X,Y,Z,1)),
14143             w = (float)(dl*func(X,Y,Z,2));
14144           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
14145           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
14146           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
14147         }
14148       } break;
14149       case 2 : { // Second order interpolation.
14150         cimg_forX(coordinates,l) {
14151           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
14152           float
14153             u0 = (float)(dl2*func(X,Y,Z,0)),
14154             v0 = (float)(dl2*func(X,Y,Z,1)),
14155             w0 = (float)(dl2*func(X,Y,Z,2));
14156           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
14157           float
14158             u = (float)(dl*func(X+u0,Y+v0,Z+w0,0)),
14159             v = (float)(dl*func(X+u0,Y+v0,Z+w0,1)),
14160             w = (float)(dl*func(X+u0,Y+v0,Z+w0,2));
14161           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
14162           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
14163           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
14164         }
14165       } break;
14166       default : { // Fourth order interpolation.
14167         cimg_forX(coordinates,x) {
14168           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
14169           float
14170             u0 = (float)(dl2*func(X,Y,Z,0)),
14171             v0 = (float)(dl2*func(X,Y,Z,1)),
14172             w0 = (float)(dl2*func(X,Y,Z,2));
14173           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
14174           float
14175             u1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,0)),
14176             v1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,1)),
14177             w1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,2));
14178           if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
14179           float
14180             u2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,0)),
14181             v2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,1)),
14182             w2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,2));
14183           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
14184           float
14185             u3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,0)),
14186             v3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,1)),
14187             w3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,2));
14188           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
14189           const float
14190             u = (u0 + u3)/3 + (u1 + u2)/1.5f,
14191             v = (v0 + v3)/3 + (v1 + v2)/1.5f,
14192             w = (w0 + w3)/3 + (w1 + w2)/1.5f;
14193           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
14194           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
14195         }
14196       }
14197       }
14198       if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
14199       return coordinates;
14200     }
14201 
14202     //! Return stream line of a vector field.
14203     static CImg<floatT> streamline(const char *const expression,
14204                                    const float x, const float y, const float z,
14205                                    const float L=256, const float dl=0.1f,
14206                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
14207                                    const bool is_oriented_only=false,
14208                                    const float x0=0, const float y0=0, const float z0=0,
14209                                    const float x1=0, const float y1=0, const float z1=0) {
14210       _functor4d_streamline_expr func(expression);
14211       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
14212     }
14213 
14214     struct _functor4d_streamline2d_directed {
14215       const CImg<T>& ref;
14216       _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
14217       float operator()(const float x, const float y, const float z, const unsigned int c) const {
14218         return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
14219       }
14220     };
14221 
14222     struct _functor4d_streamline3d_directed {
14223       const CImg<T>& ref;
14224       _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
14225       float operator()(const float x, const float y, const float z, const unsigned int c) const {
14226         return (float)ref._linear_atXYZ(x,y,z,c);
14227       }
14228     };
14229 
14230     struct _functor4d_streamline2d_oriented {
14231       const CImg<T>& ref;
14232       CImg<floatT> *pI;
14233       _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
14234       ~_functor4d_streamline2d_oriented() { if (pI) delete pI; }
14235       float operator()(const float x, const float y, const float z, const unsigned int c) const {
14236 #define _cimg_vecalign2d(i,j) if (I(i,j,0)*I(0,0,0)+I(i,j,1)*I(0,0,1)<0) { I(i,j,0) = -I(i,j,0); I(i,j,1) = -I(i,j,1); }
14237         int
14238           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
14239           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
14240           zi = (int)z;
14241         const float
14242           dx = x - xi,
14243           dy = y - yi;
14244         if (c==0) {
14245           CImg<floatT>& I = *pI;
14246           if (xi<0) xi = 0; if (nxi<0) nxi = 0;
14247           if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
14248           if (yi<0) yi = 0; if (nyi<0) nyi = 0;
14249           if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
14250           I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1);
14251           I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1);
14252           I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
14253           I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1);
14254           _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
14255         }
14256         return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
14257       }
14258     };
14259 
14260     struct _functor4d_streamline3d_oriented {
14261       const CImg<T>& ref;
14262       CImg<floatT> *pI;
14263       _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
14264       ~_functor4d_streamline3d_oriented() { if (pI) delete pI; }
14265       float operator()(const float x, const float y, const float z, const unsigned int c) const {
14266 #define _cimg_vecalign3d(i,j,k) if (I(i,j,k,0)*I(0,0,0,0)+I(i,j,k,1)*I(0,0,0,1)+I(i,j,k,2)*I(0,0,0,2)<0) { \
14267   I(i,j,k,0) = -I(i,j,k,0); I(i,j,k,1) = -I(i,j,k,1); I(i,j,k,2) = -I(i,j,k,2); }
14268         int
14269           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
14270           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
14271           zi = (int)z - (z>=0?0:1), nzi = zi + 1;
14272         const float
14273           dx = x - xi,
14274           dy = y - yi,
14275           dz = z - zi;
14276         if (c==0) {
14277           CImg<floatT>& I = *pI;
14278           if (xi<0) xi = 0; if (nxi<0) nxi = 0;
14279           if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
14280           if (yi<0) yi = 0; if (nyi<0) nyi = 0;
14281           if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
14282           if (zi<0) zi = 0; if (nzi<0) nzi = 0;
14283           if (zi>=ref.depth()) zi = ref.depth()-1; if (nzi>=ref.depth()) nzi = ref.depth()-1;
14284           I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1); I(0,0,0,2) = (float)ref(xi,yi,zi,2);
14285           I(1,0,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
14286           I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1); I(1,1,0,2) = (float)ref(nxi,nyi,zi,2);
14287           I(0,1,0,0) = (float)ref(xi,nyi,zi,0); I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,yi,zi,2);
14288           I(0,0,0,1) = (float)ref(xi,yi,nzi,0); I(0,0,0,1) = (float)ref(xi,yi,nzi,1); I(0,0,0,2) = (float)ref(xi,yi,nzi,2);
14289           I(1,0,0,1) = (float)ref(nxi,yi,nzi,0); I(1,0,0,1) = (float)ref(nxi,yi,nzi,1); I(1,0,0,2) = (float)ref(nxi,yi,nzi,2);
14290           I(1,1,0,1) = (float)ref(nxi,nyi,nzi,0); I(1,1,0,1) = (float)ref(nxi,nyi,nzi,1); I(1,1,0,2) = (float)ref(nxi,nyi,nzi,2);
14291           I(0,1,0,1) = (float)ref(xi,nyi,nzi,0); I(0,1,0,1) = (float)ref(xi,nyi,nzi,1); I(0,1,0,2) = (float)ref(xi,yi,nzi,2);
14292           _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
14293           _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
14294         }
14295         return (float)pI->_linear_atXYZ(dx,dy,dz,c);
14296       }
14297     };
14298 
14299     struct _functor4d_streamline_expr {
14300       _cimg_math_parser *mp;
14301       ~_functor4d_streamline_expr() { if (mp) delete mp; }
14302       _functor4d_streamline_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,"streamline"); }
14303       float operator()(const float x, const float y, const float z, const unsigned int c) const {
14304         return (float)mp->eval(x,y,z,c);
14305       }
14306     };
14307 
14308     //! Return a vector with specified coefficients.
14309     static CImg<T> vector(const T& a0) {
14310       static CImg<T> r(1,1); r[0] = a0;
14311       return r;
14312     }
14313 
14314     //! Return a vector with specified coefficients.
14315     static CImg<T> vector(const T& a0, const T& a1) {
14316       static CImg<T> r(1,2); T *ptr = r._data;
14317       *(ptr++) = a0; *(ptr++) = a1;
14318       return r;
14319     }
14320 
14321     //! Return a vector with specified coefficients.
14322     static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
14323       static CImg<T> r(1,3); T *ptr = r._data;
14324       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
14325       return r;
14326     }
14327 
14328     //! Return a vector with specified coefficients.
14329     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
14330       static CImg<T> r(1,4); T *ptr = r._data;
14331       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14332       return r;
14333     }
14334 
14335     //! Return a vector with specified coefficients.
14336     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
14337       static CImg<T> r(1,5); T *ptr = r._data;
14338       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
14339       return r;
14340     }
14341 
14342     //! Return a vector with specified coefficients.
14343     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
14344       static CImg<T> r(1,6); T *ptr = r._data;
14345       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
14346       return r;
14347     }
14348 
14349     //! Return a vector with specified coefficients.
14350     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14351                           const T& a4, const T& a5, const T& a6) {
14352       static CImg<T> r(1,7); T *ptr = r._data;
14353       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14354       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6;
14355       return r;
14356     }
14357 
14358     //! Return a vector with specified coefficients.
14359     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14360                           const T& a4, const T& a5, const T& a6, const T& a7) {
14361       static CImg<T> r(1,8); T *ptr = r._data;
14362       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14363       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14364       return r;
14365     }
14366 
14367     //! Return a vector with specified coefficients.
14368     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14369                           const T& a4, const T& a5, const T& a6, const T& a7,
14370                           const T& a8) {
14371       static CImg<T> r(1,9); T *ptr = r._data;
14372       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14373       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14374       *(ptr++) = a8;
14375       return r;
14376     }
14377 
14378     //! Return a vector with specified coefficients.
14379     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14380                           const T& a4, const T& a5, const T& a6, const T& a7,
14381                           const T& a8, const T& a9) {
14382       static CImg<T> r(1,10); T *ptr = r._data;
14383       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14384       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14385       *(ptr++) = a8; *(ptr++) = a9;
14386       return r;
14387     }
14388 
14389     //! Return a vector with specified coefficients.
14390     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14391                           const T& a4, const T& a5, const T& a6, const T& a7,
14392                           const T& a8, const T& a9, const T& a10) {
14393       static CImg<T> r(1,11); T *ptr = r._data;
14394       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14395       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14396       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10;
14397       return r;
14398     }
14399 
14400     //! Return a vector with specified coefficients.
14401     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14402                           const T& a4, const T& a5, const T& a6, const T& a7,
14403                           const T& a8, const T& a9, const T& a10, const T& a11) {
14404       static CImg<T> r(1,12); T *ptr = r._data;
14405       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14406       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14407       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
14408       return r;
14409     }
14410 
14411     //! Return a vector with specified coefficients.
14412     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14413                           const T& a4, const T& a5, const T& a6, const T& a7,
14414                           const T& a8, const T& a9, const T& a10, const T& a11,
14415                           const T& a12) {
14416       static CImg<T> r(1,13); T *ptr = r._data;
14417       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14418       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14419       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
14420       *(ptr++) = a12;
14421       return r;
14422     }
14423 
14424     //! Return a vector with specified coefficients.
14425     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14426                           const T& a4, const T& a5, const T& a6, const T& a7,
14427                           const T& a8, const T& a9, const T& a10, const T& a11,
14428                           const T& a12, const T& a13) {
14429       static CImg<T> r(1,14); T *ptr = r._data;
14430       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14431       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14432       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
14433       *(ptr++) = a12; *(ptr++) = a13;
14434       return r;
14435     }
14436 
14437     //! Return a vector with specified coefficients.
14438     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14439                           const T& a4, const T& a5, const T& a6, const T& a7,
14440                           const T& a8, const T& a9, const T& a10, const T& a11,
14441                           const T& a12, const T& a13, const T& a14) {
14442       static CImg<T> r(1,15); T *ptr = r._data;
14443       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14444       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14445       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
14446       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
14447       return r;
14448     }
14449 
14450     //! Return a vector with specified coefficients.
14451     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
14452                           const T& a4, const T& a5, const T& a6, const T& a7,
14453                           const T& a8, const T& a9, const T& a10, const T& a11,
14454                           const T& a12, const T& a13, const T& a14, const T& a15) {
14455       static CImg<T> r(1,16); T *ptr = r._data;
14456       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14457       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14458       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
14459       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
14460       return r;
14461     }
14462 
14463     //! Return a 1x1 square matrix with specified coefficients.
14464     static CImg<T> matrix(const T& a0) {
14465       return vector(a0);
14466     }
14467 
14468     //! Return a 2x2 square matrix with specified coefficients.
14469     static CImg<T> matrix(const T& a0, const T& a1,
14470                           const T& a2, const T& a3) {
14471       static CImg<T> r(2,2); T *ptr = r._data;
14472       *(ptr++) = a0; *(ptr++) = a1;
14473       *(ptr++) = a2; *(ptr++) = a3;
14474       return r;
14475     }
14476 
14477     //! Return a 3x3 square matrix with specified coefficients.
14478     static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
14479                           const T& a3, const T& a4, const T& a5,
14480                           const T& a6, const T& a7, const T& a8) {
14481       static CImg<T> r(3,3); T *ptr = r._data;
14482       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
14483       *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
14484       *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
14485       return r;
14486     }
14487 
14488     //! Return a 4x4 square matrix with specified coefficients.
14489     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
14490                           const T& a4, const T& a5, const T& a6, const T& a7,
14491                           const T& a8, const T& a9, const T& a10, const T& a11,
14492                           const T& a12, const T& a13, const T& a14, const T& a15) {
14493       static CImg<T> r(4,4); T *ptr = r._data;
14494       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
14495       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
14496       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
14497       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
14498       return r;
14499     }
14500 
14501     //! Return a 5x5 square matrix with specified coefficients.
14502     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
14503                           const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
14504                           const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
14505                           const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
14506                           const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
14507       static CImg<T> r(5,5); T *ptr = r._data;
14508       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
14509       *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
14510       *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
14511       *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
14512       *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
14513       return r;
14514     }
14515 
14516     //! Return a 1x1 symmetric matrix with specified coefficients.
14517     static CImg<T> tensor(const T& a1) {
14518       return matrix(a1);
14519     }
14520 
14521     //! Return a 2x2 symmetric matrix tensor with specified coefficients.
14522     static CImg<T> tensor(const T& a1, const T& a2, const T& a3) {
14523       return matrix(a1,a2,a2,a3);
14524     }
14525 
14526     //! Return a 3x3 symmetric matrix with specified coefficients.
14527     static CImg<T> tensor(const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6) {
14528       return matrix(a1,a2,a3,a2,a4,a5,a3,a5,a6);
14529     }
14530 
14531     //! Return a 1x1 diagonal matrix with specified coefficients.
14532     static CImg<T> diagonal(const T& a0) {
14533       return matrix(a0);
14534     }
14535 
14536     //! Return a 2x2 diagonal matrix with specified coefficients.
14537     static CImg<T> diagonal(const T& a0, const T& a1) {
14538       return matrix(a0,0,0,a1);
14539     }
14540 
14541     //! Return a 3x3 diagonal matrix with specified coefficients.
14542     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
14543       return matrix(a0,0,0,0,a1,0,0,0,a2);
14544     }
14545 
14546     //! Return a 4x4 diagonal matrix with specified coefficients.
14547     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
14548       return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
14549     }
14550 
14551     //! Return a 5x5 diagonal matrix with specified coefficients.
14552     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
14553       return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4);
14554     }
14555 
14556     //! Return a NxN identity matrix.
14557     static CImg<T> identity_matrix(const unsigned int N) {
14558       CImg<T> res(N,N,1,1,0);
14559       cimg_forX(res,x) res(x,x) = 1;
14560       return res;
14561     }
14562 
14563     //! Return a N-numbered sequence vector from \p a0 to \p a1.
14564     static CImg<T> sequence(const unsigned int N, const T a0, const T a1) {
14565       if (N) return CImg<T>(1,N).sequence(a0,a1);
14566       return CImg<T>();
14567     }
14568 
14569     //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w.
14570     static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w, const bool quaternion_data=false) {
14571       float X,Y,Z,W;
14572       if (!quaternion_data) {
14573         const float norm = (float)std::sqrt(x*x + y*y + z*z),
14574           nx = norm>0?x/norm:0,
14575           ny = norm>0?y/norm:0,
14576           nz = norm>0?z/norm:1,
14577           nw = norm>0?w:0,
14578           sina = (float)std::sin(nw/2),
14579           cosa = (float)std::cos(nw/2);
14580         X = nx*sina;
14581         Y = ny*sina;
14582         Z = nz*sina;
14583         W = cosa;
14584       } else {
14585         const float norm = (float)std::sqrt(x*x + y*y + z*z + w*w);
14586         if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; }
14587         else { X = Y = Z = 0; W = 1; }
14588       }
14589       const float xx = X*X, xy = X*Y, xz = X*Z, xw = X*W, yy = Y*Y, yz = Y*Z, yw = Y*W, zz = Z*Z, zw = Z*W;
14590       return CImg<T>::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)),   (T)(2*(xz-yw)),
14591                              (T)(2*(xy-zw)),   (T)(1-2*(xx+zz)), (T)(2*(yz+xw)),
14592                              (T)(2*(xz+yw)),   (T)(2*(yz-xw)),   (T)(1-2*(xx+yy)));
14593     }
14594 
14595     //@}
14596     //-----------------------------------
14597     //
14598     //! \name Value Manipulation
14599     //@{
14600     //-----------------------------------
14601 
14602     //! Fill an image by a value \p val.
14603     /**
14604        \param val = fill value
14605        \note All pixel values of the instance image will be initialized by \p val.
14606     **/
14607     CImg<T>& fill(const T val) {
14608       if (is_empty()) return *this;
14609       if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
14610       else std::memset(_data,(int)val,size()*sizeof(T));
14611       return *this;
14612     }
14613 
14614     CImg<T> get_fill(const T val) const {
14615       return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
14616     }
14617 
14618     //! Fill sequentially all pixel values with values \a val0 and \a val1 respectively.
14619     CImg<T>& fill(const T val0, const T val1) {
14620       if (is_empty()) return *this;
14621       T *ptrd, *ptre = end()-1;
14622       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
14623       if (ptrd!=ptre+1) *(ptrd++) = val0;
14624       return *this;
14625     }
14626 
14627     CImg<T> get_fill(const T val0, const T val1) const {
14628       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
14629     }
14630 
14631     //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2.
14632     CImg<T>& fill(const T val0, const T val1, const T val2) {
14633       if (is_empty()) return *this;
14634       T *ptrd, *ptre = end()-2;
14635       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
14636       ptre+=2;
14637       switch (ptre - ptrd) {
14638       case 2 : *(--ptre) = val1;
14639       case 1 : *(--ptre) = val0;
14640       }
14641       return *this;
14642     }
14643 
14644     CImg<T> get_fill(const T val0, const T val1, const T val2) const {
14645       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
14646     }
14647 
14648     //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3.
14649     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3) {
14650       if (is_empty()) return *this;
14651       T *ptrd, *ptre = end()-3;
14652       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
14653       ptre+=3;
14654       switch (ptre - ptrd) {
14655       case 3 : *(--ptre) = val2;
14656       case 2 : *(--ptre) = val1;
14657       case 1 : *(--ptre) = val0;
14658       }
14659       return *this;
14660     }
14661 
14662     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3) const {
14663       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
14664     }
14665 
14666     //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4.
14667     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4) {
14668       if (is_empty()) return *this;
14669       T *ptrd, *ptre = end()-4;
14670       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; }
14671       ptre+=4;
14672       switch (ptre - ptrd) {
14673       case 4 : *(--ptre) = val3;
14674       case 3 : *(--ptre) = val2;
14675       case 2 : *(--ptre) = val1;
14676       case 1 : *(--ptre) = val0;
14677       }
14678       return *this;
14679     }
14680 
14681     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const {
14682       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
14683     }
14684 
14685     //! Fill sequentially all pixel values with values \a val0 and \a val1 and \a val2 and \a val3 and \a val4 and \a val5.
14686     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) {
14687       if (is_empty()) return *this;
14688       T *ptrd, *ptre = end()-5;
14689       for (ptrd = _data; ptrd<ptre; ) {
14690         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
14691       }
14692       ptre+=5;
14693       switch (ptre - ptrd) {
14694       case 5 : *(--ptre) = val4;
14695       case 4 : *(--ptre) = val3;
14696       case 3 : *(--ptre) = val2;
14697       case 2 : *(--ptre) = val1;
14698       case 1 : *(--ptre) = val0;
14699       }
14700       return *this;
14701     }
14702 
14703     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const {
14704       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
14705     }
14706 
14707     //! Fill sequentially pixel values.
14708     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) {
14709       if (is_empty()) return *this;
14710       T *ptrd, *ptre = end()-6;
14711       for (ptrd = _data; ptrd<ptre; ) {
14712         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6;
14713       }
14714       ptre+=6;
14715       switch (ptre - ptrd) {
14716       case 6 : *(--ptre) = val5;
14717       case 5 : *(--ptre) = val4;
14718       case 4 : *(--ptre) = val3;
14719       case 3 : *(--ptre) = val2;
14720       case 2 : *(--ptre) = val1;
14721       case 1 : *(--ptre) = val0;
14722       }
14723       return *this;
14724     }
14725 
14726     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) const {
14727       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
14728     }
14729 
14730     //! Fill sequentially pixel values.
14731     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14732                   const T val7) {
14733       if (is_empty()) return *this;
14734       T *ptrd, *ptre = end()-7;
14735       for (ptrd = _data; ptrd<ptre; ) {
14736         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
14737         *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
14738       }
14739       ptre+=7;
14740       switch (ptre - ptrd) {
14741       case 7 : *(--ptre) = val6;
14742       case 6 : *(--ptre) = val5;
14743       case 5 : *(--ptre) = val4;
14744       case 4 : *(--ptre) = val3;
14745       case 3 : *(--ptre) = val2;
14746       case 2 : *(--ptre) = val1;
14747       case 1 : *(--ptre) = val0;
14748       }
14749       return *this;
14750     }
14751 
14752     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14753                      const T val7) const {
14754       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
14755     }
14756 
14757     //! Fill sequentially pixel values.
14758     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14759                   const T val7, const T val8) {
14760       if (is_empty()) return *this;
14761       T *ptrd, *ptre = end()-8;
14762       for (ptrd = _data; ptrd<ptre; ) {
14763         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
14764         *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
14765         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
14766       }
14767       ptre+=8;
14768       switch (ptre - ptrd) {
14769       case 8 : *(--ptre) = val7;
14770       case 7 : *(--ptre) = val6;
14771       case 6 : *(--ptre) = val5;
14772       case 5 : *(--ptre) = val4;
14773       case 4 : *(--ptre) = val3;
14774       case 3 : *(--ptre) = val2;
14775       case 2 : *(--ptre) = val1;
14776       case 1 : *(--ptre) = val0;
14777       }
14778       return *this;
14779     }
14780 
14781     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14782                      const T val7, const T val8) const {
14783       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
14784     }
14785 
14786     //! Fill sequentially pixel values.
14787     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14788                   const T val7, const T val8, const T val9) {
14789       if (is_empty()) return *this;
14790       T *ptrd, *ptre = end()-9;
14791       for (ptrd = _data; ptrd<ptre; ) {
14792         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
14793         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
14794       }
14795       ptre+=9;
14796       switch (ptre - ptrd) {
14797       case 9 : *(--ptre) = val8;
14798       case 8 : *(--ptre) = val7;
14799       case 7 : *(--ptre) = val6;
14800       case 6 : *(--ptre) = val5;
14801       case 5 : *(--ptre) = val4;
14802       case 4 : *(--ptre) = val3;
14803       case 3 : *(--ptre) = val2;
14804       case 2 : *(--ptre) = val1;
14805       case 1 : *(--ptre) = val0;
14806       }
14807       return *this;
14808     }
14809 
14810     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14811                      const T val7, const T val8, const T val9) const {
14812       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
14813     }
14814 
14815     //! Fill sequentially pixel values.
14816     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14817                   const T val7, const T val8, const T val9, const T val10) {
14818       if (is_empty()) return *this;
14819       T *ptrd, *ptre = end()-10;
14820       for (ptrd = _data; ptrd<ptre; ) {
14821         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
14822         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
14823         *(ptrd++) = val10;
14824       }
14825       ptre+=10;
14826       switch (ptre - ptrd) {
14827       case 10 : *(--ptre) = val9;
14828       case 9 : *(--ptre) = val8;
14829       case 8 : *(--ptre) = val7;
14830       case 7 : *(--ptre) = val6;
14831       case 6 : *(--ptre) = val5;
14832       case 5 : *(--ptre) = val4;
14833       case 4 : *(--ptre) = val3;
14834       case 3 : *(--ptre) = val2;
14835       case 2 : *(--ptre) = val1;
14836       case 1 : *(--ptre) = val0;
14837       }
14838       return *this;
14839     }
14840 
14841     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14842                      const T val7, const T val8, const T val9, const T val10) const {
14843       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
14844     }
14845 
14846     //! Fill sequentially pixel values.
14847     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14848                   const T val7, const T val8, const T val9, const T val10, const T val11) {
14849       if (is_empty()) return *this;
14850       T *ptrd, *ptre = end()-11;
14851       for (ptrd = _data; ptrd<ptre; ) {
14852         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
14853         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
14854       }
14855       ptre+=11;
14856       switch (ptre - ptrd) {
14857       case 11 : *(--ptre) = val10;
14858       case 10 : *(--ptre) = val9;
14859       case 9 : *(--ptre) = val8;
14860       case 8 : *(--ptre) = val7;
14861       case 7 : *(--ptre) = val6;
14862       case 6 : *(--ptre) = val5;
14863       case 5 : *(--ptre) = val4;
14864       case 4 : *(--ptre) = val3;
14865       case 3 : *(--ptre) = val2;
14866       case 2 : *(--ptre) = val1;
14867       case 1 : *(--ptre) = val0;
14868       }
14869       return *this;
14870     }
14871 
14872     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14873                      const T val7, const T val8, const T val9, const T val10, const T val11) const {
14874       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11);
14875     }
14876 
14877     //! Fill sequentially pixel values.
14878     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14879                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) {
14880       if (is_empty()) return *this;
14881       T *ptrd, *ptre = end()-12;
14882       for (ptrd = _data; ptrd<ptre; ) {
14883         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
14884         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
14885         *(ptrd++) = val12;
14886       }
14887       ptre+=12;
14888       switch (ptre - ptrd) {
14889       case 12 : *(--ptre) = val11;
14890       case 11 : *(--ptre) = val10;
14891       case 10 : *(--ptre) = val9;
14892       case 9 : *(--ptre) = val8;
14893       case 8 : *(--ptre) = val7;
14894       case 7 : *(--ptre) = val6;
14895       case 6 : *(--ptre) = val5;
14896       case 5 : *(--ptre) = val4;
14897       case 4 : *(--ptre) = val3;
14898       case 3 : *(--ptre) = val2;
14899       case 2 : *(--ptre) = val1;
14900       case 1 : *(--ptre) = val0;
14901       }
14902       return *this;
14903     }
14904 
14905     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14906                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) const {
14907       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12);
14908     }
14909 
14910     //! Fill sequentially pixel values.
14911     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14912                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
14913                   const T val13) {
14914       if (is_empty()) return *this;
14915       T *ptrd, *ptre = end()-13;
14916       for (ptrd = _data; ptrd<ptre; ) {
14917         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
14918         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
14919         *(ptrd++) = val12; *(ptrd++) = val13;
14920       }
14921       ptre+=13;
14922       switch (ptre - ptrd) {
14923       case 13 : *(--ptre) = val12;
14924       case 12 : *(--ptre) = val11;
14925       case 11 : *(--ptre) = val10;
14926       case 10 : *(--ptre) = val9;
14927       case 9 : *(--ptre) = val8;
14928       case 8 : *(--ptre) = val7;
14929       case 7 : *(--ptre) = val6;
14930       case 6 : *(--ptre) = val5;
14931       case 5 : *(--ptre) = val4;
14932       case 4 : *(--ptre) = val3;
14933       case 3 : *(--ptre) = val2;
14934       case 2 : *(--ptre) = val1;
14935       case 1 : *(--ptre) = val0;
14936       }
14937       return *this;
14938     }
14939 
14940     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14941                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
14942                      const T val13) const {
14943       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
14944                                                   val13);
14945     }
14946 
14947     //! Fill sequentially pixel values.
14948     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14949                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
14950                   const T val13, const T val14) {
14951       if (is_empty()) return *this;
14952       T *ptrd, *ptre = end()-14;
14953       for (ptrd = _data; ptrd<ptre; ) {
14954         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
14955         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
14956         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
14957       }
14958       ptre+=14;
14959       switch (ptre - ptrd) {
14960       case 14 : *(--ptre) = val13;
14961       case 13 : *(--ptre) = val12;
14962       case 12 : *(--ptre) = val11;
14963       case 11 : *(--ptre) = val10;
14964       case 10 : *(--ptre) = val9;
14965       case 9 : *(--ptre) = val8;
14966       case 8 : *(--ptre) = val7;
14967       case 7 : *(--ptre) = val6;
14968       case 6 : *(--ptre) = val5;
14969       case 5 : *(--ptre) = val4;
14970       case 4 : *(--ptre) = val3;
14971       case 3 : *(--ptre) = val2;
14972       case 2 : *(--ptre) = val1;
14973       case 1 : *(--ptre) = val0;
14974       }
14975       return *this;
14976     }
14977 
14978     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14979                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
14980                      const T val13, const T val14) const {
14981       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
14982                                                   val13,val14);
14983     }
14984 
14985     //! Fill sequentially pixel values.
14986     CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
14987                   const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
14988                   const T val13, const T val14, const T val15) {
14989       if (is_empty()) return *this;
14990       T *ptrd, *ptre = end()-15;
14991       for (ptrd = _data; ptrd<ptre; ) {
14992         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
14993         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
14994         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
14995       }
14996       ptre+=15;
14997       switch (ptre - ptrd) {
14998       case 15 : *(--ptre) = val14;
14999       case 14 : *(--ptre) = val13;
15000       case 13 : *(--ptre) = val12;
15001       case 12 : *(--ptre) = val11;
15002       case 11 : *(--ptre) = val10;
15003       case 10 : *(--ptre) = val9;
15004       case 9 : *(--ptre) = val8;
15005       case 8 : *(--ptre) = val7;
15006       case 7 : *(--ptre) = val6;
15007       case 6 : *(--ptre) = val5;
15008       case 5 : *(--ptre) = val4;
15009       case 4 : *(--ptre) = val3;
15010       case 3 : *(--ptre) = val2;
15011       case 2 : *(--ptre) = val1;
15012       case 1 : *(--ptre) = val0;
15013       }
15014       return *this;
15015     }
15016 
15017     CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
15018                      const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
15019                      const T val13, const T val14, const T val15) const {
15020       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
15021                                                   val13,val14,val15);
15022     }
15023 
15024     //! Fill image values according to the given expression, which can be a formula or a list of values.
15025     CImg<T>& fill(const char *const expression, const bool repeat_flag) {
15026       if (is_empty() || !expression || !*expression) return *this;
15027       const unsigned int omode = cimg::exception_mode();
15028       cimg::exception_mode() = 0;
15029       try { // Try to fill values according to a formula.
15030         const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
15031         _cimg_math_parser mp(base,expression,"fill");
15032         T *ptrd = _data;
15033         cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp.eval((double)x,(double)y,(double)z,(double)c);
15034       } catch (CImgException& e) { // If failed, try to recognize a list of values.
15035         char item[16384] = { 0 }, sep = 0; const char *nexpression = expression;
15036         unsigned int nb = 0; const unsigned int siz = size();
15037         T *ptrd = _data;
15038         for (double val = 0; *nexpression && nb<siz; ++nb) {
15039           const int err = std::sscanf(nexpression,"%4095[ \n\t0-9.e+-]%c",item,&sep);
15040           if (err>0 && std::sscanf(item,"%lf",&val)==1) {
15041             nexpression+=std::strlen(item) + (err>1?1:0);
15042             *(ptrd++) = (T)val;
15043           } else break;
15044         }
15045         cimg::exception_mode() = omode;
15046         if (*nexpression && nb<siz)
15047           throw CImgArgumentException(e.what(),pixel_type(),expression);
15048         if (repeat_flag && nb && nb<siz) for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
15049       }
15050       cimg::exception_mode() = omode;
15051       return *this;
15052     }
15053 
15054     CImg<T> get_fill(const char *const values, const bool repeat_values) const {
15055       return (+*this).fill(values,repeat_values);
15056     }
15057 
15058     //! Fill image values according to the values found in the specified image.
15059     template<typename t>
15060     CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
15061       if (is_empty() || !values) return *this;
15062       T *ptrd = _data, *ptre = ptrd + size();
15063       for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs) *(ptrd++) = (T)*ptrs;
15064       if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
15065       return *this;
15066     }
15067 
15068     template<typename t>
15069     CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
15070       return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):(+*this).fill(values,repeat_values);
15071     }
15072 
15073     //! Fill image values along the X-axis at the specified pixel position (y,z,c).
15074     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
15075 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
15076     va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
15077     for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
15078     va_end(ap); }
15079       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
15080       return *this;
15081     }
15082 
15083     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
15084       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
15085       return *this;
15086     }
15087 
15088     //! Fill image values along the Y-axis at the specified pixel position (x,z,c).
15089     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
15090       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
15091       return *this;
15092     }
15093 
15094     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
15095       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
15096       return *this;
15097     }
15098 
15099     //! Fill image values along the Z-axis at the specified pixel position (x,y,c).
15100     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
15101       const unsigned int wh = _width*_height;
15102       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
15103       return *this;
15104     }
15105 
15106     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
15107       const unsigned int wh = _width*_height;
15108       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
15109       return *this;
15110     }
15111 
15112     //! Fill image values along the C-axis at the specified pixel position (x,y,z).
15113     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
15114       const unsigned int whd = _width*_height*_depth;
15115       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
15116       return *this;
15117     }
15118 
15119     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
15120       const unsigned int whd = _width*_height*_depth;
15121       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
15122       return *this;
15123     }
15124 
15125     //! Invert endianness of the image buffer.
15126     CImg<T>& invert_endianness() {
15127       cimg::invert_endianness(_data,size());
15128       return *this;
15129     }
15130 
15131     CImg<T> get_invert_endianness() const {
15132       return (+*this).invert_endianness();
15133     }
15134 
15135     //! Fill the instance image with random values between specified range.
15136     CImg<T>& rand(const T val_min, const T val_max) {
15137       const float delta = (float)val_max - (float)val_min;
15138       cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta);
15139       return *this;
15140     }
15141 
15142     CImg<T> get_rand(const T val_min, const T val_max) const {
15143       return (+*this).rand(val_min,val_max);
15144     }
15145 
15146     //! Compute image with rounded pixel values.
15147     /**
15148        \param x Rounding precision.
15149        \param rounding_type Roundin type, can be 0 (nearest), 1 (forward), -1(backward).
15150     **/
15151     CImg<T>& round(const float x, const int rounding_type=0) {
15152       if (x>0) cimg_for(*this,ptrd,T) *ptrd = (T)cimg::round(*ptrd,x,rounding_type);
15153       return *this;
15154     }
15155 
15156     CImg<T> get_round(const float x, const unsigned int rounding_type=0) const {
15157       return (+*this).round(x,rounding_type);
15158     }
15159 
15160     //! Add random noise to the values of the instance image.
15161     /**
15162        \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the global value range.
15163        \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper, \p 3=Poisson or \p 4=Rician).
15164        \return A reference to the modified instance image.
15165        \note
15166        - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on the image value itself.
15167        - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the instance image.
15168        \par Sample code :
15169        \code
15170        const CImg<float> img("reference.jpg"), res = img.get_noise(40);
15171        (img,res.normalize(0,255)).display();
15172        \endcode
15173        \image html ref_noise.jpg
15174     **/
15175     CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
15176       if (!is_empty()) {
15177         double nsigma = sigma, max = (double)cimg::type<T>::max(), min = (double)cimg::type<T>::min();
15178         Tfloat m = 0, M = 0;
15179         if (nsigma==0 && noise_type!=3) return *this;
15180         if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
15181         if (nsigma<0) nsigma = -nsigma*(M-m)/100.0;
15182         switch (noise_type) {
15183         case 0 : { // Gaussian noise
15184           cimg_for(*this,ptrd,T) {
15185             double val = *ptrd + nsigma*cimg::grand();
15186             if (val>max) val = max;
15187             if (val<min) val = min;
15188             *ptrd = (T)val;
15189           }
15190         } break;
15191         case 1 : { // Uniform noise
15192           cimg_for(*this,ptrd,T) {
15193             double val = *ptrd + nsigma*cimg::crand();
15194             if (val>max) val = max;
15195             if (val<min) val = min;
15196             *ptrd = (T)val;
15197           }
15198         } break;
15199         case 2 : { // Salt & Pepper noise
15200           if (nsigma<0) nsigma = -nsigma;
15201           if (M==m) { m = 0; M = (float)(cimg::type<T>::is_float()?1:cimg::type<T>::max()); }
15202           cimg_for(*this,ptrd,T) if (cimg::rand()*100<nsigma) *ptrd = (T)(cimg::rand()<0.5?M:m);
15203         } break;
15204 
15205         case 3 : { // Poisson Noise
15206           cimg_for(*this,ptrd,T) *ptrd = (T)cimg::prand(*ptrd);
15207         } break;
15208 
15209         case 4 : { // Rice noise
15210           const double sqrt2 = (double)std::sqrt(2.0);
15211           cimg_for(*this,ptrd,T) {
15212             const double
15213               val0 = (double)*ptrd/sqrt2,
15214               re = val0 + nsigma*cimg::grand(),
15215               im = val0 + nsigma*cimg::grand();
15216             double val = std::sqrt(re*re + im*im);
15217             if (val>max) val = max;
15218             if (val<min) val = min;
15219             *ptrd = (T)val;
15220           }
15221         } break;
15222         default :
15223           throw CImgArgumentException(_cimg_instance
15224                                       "noise() : Invalid specified noise type %d "
15225                                       "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
15226                                       cimg_instance,
15227                                       noise_type);
15228         }
15229       }
15230       return *this;
15231     }
15232 
15233     CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
15234       return (+*this).noise(sigma,noise_type);
15235     }
15236 
15237     //! Linearly normalize values of the instance image between \p value_min and \p value_max.
15238     /**
15239        \param value_min Minimum desired value of the resulting image.
15240        \param value_max Maximum desired value of the resulting image.
15241        \return A reference to the modified instance image.
15242        \note
15243        - Function \p CImg<T>::get_normalize() is also defined. It returns a non-shared modified copy of the instance image.
15244        \par Sample code :
15245        \code
15246        const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
15247        (img,res).display();
15248        \endcode
15249        \image html ref_normalize2.jpg
15250     **/
15251     CImg<T>& normalize(const T value_min, const T value_max) {
15252       if (is_empty()) return *this;
15253       const T a = value_min<value_max?value_min:value_max, b = value_min<value_max?value_max:value_min;
15254       T m, M = max_min(m);
15255       const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
15256       if (m==M) return fill(0);
15257       if (m!=a || M!=b) cimg_for(*this,ptrd,T) *ptrd = (T)((*ptrd-fm)/(fM-fm)*(b-a)+a);
15258       return *this;
15259     }
15260 
15261     CImg<Tfloat> get_normalize(const T value_min, const T value_max) const {
15262       return CImg<Tfloat>(*this,false).normalize((Tfloat)value_min,(Tfloat)value_max);
15263     }
15264 
15265     //! Normalize multi-valued pixels of the instance image, with respect to their L2-norm.
15266     /**
15267        \return A reference to the modified instance image.
15268        \note
15269        - Function \p CImg<T>::get_normalize() is also defined. It returns a non-shared modified copy of the instance image.
15270        \par Sample code :
15271        \code
15272        const CImg<float> img("reference.jpg"), res = img.get_normalize();
15273        (img,res.normalize(0,255)).display();
15274        \endcode
15275        \image html ref_normalize.jpg
15276     **/
15277     CImg<T>& normalize() {
15278       T *ptrd = _data;
15279       const unsigned int whd = _width*_height*_depth;
15280       cimg_forXYZ(*this,x,y,z) {
15281         const T *ptrs = ptrd;
15282         float n = 0;
15283         cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
15284         n = (float)std::sqrt(n);
15285         T *_ptrd = ptrd++;
15286         if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
15287         else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
15288       }
15289       return *this;
15290     }
15291 
15292     CImg<Tfloat> get_normalize() const {
15293       return CImg<Tfloat>(*this,false).normalize();
15294     }
15295 
15296     //! Compute L2-norm of each multi-valued pixel of the instance image.
15297     /**
15298        \param norm_type Type of computed vector norm (can be \p 0=Linf, \p 1=L1 or \p 2=L2).
15299        \return A reference to the modified instance image.
15300        \note
15301        - Function \p CImg<T>::get_norm() is also defined. It returns a non-shared modified copy of the instance image.
15302        \par Sample code :
15303        \code
15304        const CImg<float> img("reference.jpg"), res = img.get_norm();
15305        (img,res.normalize(0,255)).display();
15306        \endcode
15307        \image html ref_norm.jpg
15308     **/
15309     CImg<T>& norm(const int norm_type=2) {
15310       if (_spectrum==1) return abs();
15311       return get_norm(norm_type).move_to(*this);
15312     }
15313 
15314     CImg<Tfloat> get_norm(const int norm_type=2) const {
15315       if (is_empty()) return *this;
15316       if (_spectrum==1) return get_abs();
15317       const T *ptrs = _data;
15318       const unsigned int whd = _width*_height*_depth;
15319       CImg<Tfloat> res(_width,_height,_depth);
15320       Tfloat *ptrd = res._data;
15321       switch (norm_type) {
15322       case -1 : {             // Linf norm
15323         cimg_forXYZ(*this,x,y,z) {
15324           Tfloat n = 0;
15325           const T *_ptrs = ptrs++;
15326           cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
15327           *(ptrd++) = n;
15328         }
15329       } break;
15330       case 1 : {              // L1 norm
15331         cimg_forXYZ(*this,x,y,z) {
15332           Tfloat n = 0;
15333           const T *_ptrs = ptrs++;
15334           cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
15335           *(ptrd++) = n;
15336         }
15337       } break;
15338       default : {             // L2 norm
15339         cimg_forXYZ(*this,x,y,z) {
15340           Tfloat n = 0;
15341           const T *_ptrs = ptrs++;
15342           cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
15343           *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
15344         }
15345       }
15346       }
15347       return res;
15348     }
15349 
15350     //! Cut values of the instance image between \p value_min and \p value_max.
15351     /**
15352        \param value_min Minimum desired value of the resulting image.
15353        \param value_max Maximum desired value of the resulting image.
15354        \return A reference to the modified instance image.
15355        \note
15356        - Function \p CImg<T>::get_cut() is also defined. It returns a non-shared modified copy of the instance image.
15357        \par Sample code :
15358        \code
15359        const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
15360        (img,res).display();
15361        \endcode
15362        \image html ref_cut.jpg
15363     **/
15364     CImg<T>& cut(const T value_min, const T value_max) {
15365       if (is_empty()) return *this;
15366       const T a = value_min<value_max?value_min:value_max, b = value_min<value_max?value_max:value_min;
15367       cimg_for(*this,ptrd,T) *ptrd = (*ptrd<a)?a:((*ptrd>b)?b:*ptrd);
15368       return *this;
15369     }
15370 
15371     CImg<T> get_cut(const T value_min, const T value_max) const {
15372       return (+*this).cut(value_min,value_max);
15373     }
15374 
15375     //! Uniformly quantize values of the instance image into \p nb_levels levels.
15376     /**
15377        \param nb_levels Number of quantization levels.
15378        \param keep_range Tells if resulting values keep the same range as the original ones.
15379        \return A reference to the modified instance image.
15380        \note
15381        - Function \p CImg<T>::get_quantize() is also defined. It returns a non-shared modified copy of the instance image.
15382        \par Sample code :
15383        \code
15384        const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
15385        (img,res).display();
15386        \endcode
15387        \image html ref_quantize.jpg
15388     **/
15389     CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
15390       if (!nb_levels)
15391         throw CImgArgumentException(_cimg_instance
15392                                     "quantize() : Invalid quantization request with 0 values.",
15393                                     cimg_instance);
15394 
15395       if (is_empty()) return *this;
15396       Tfloat m, M = (Tfloat)max_min(m), range = M - m;
15397       if (range>0) {
15398         if (keep_range) cimg_for(*this,ptrd,T) {
15399           const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
15400           *ptrd = (T)(m + cimg::min(val,nb_levels-1)*range/nb_levels);
15401         } else cimg_for(*this,ptrd,T) {
15402           const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
15403           *ptrd = (T)cimg::min(val,nb_levels-1);
15404         }
15405       }
15406       return *this;
15407     }
15408 
15409     CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
15410       return (+*this).quantize(n,keep_range);
15411     }
15412 
15413     //! Threshold values of the instance image.
15414     /**
15415        \param value Threshold value
15416        \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
15417        \param strict_threshold Tells if threshold value is strict.
15418        \return A reference to the modified instance image. Resulting pixel values are either equal to 0 or 1.
15419        \note
15420        - Function \p CImg<T>::get_threshold() is also defined. It returns a non-shared modified copy of the instance image.
15421        \par Sample code :
15422        \code
15423        const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
15424        (img,res.normalize(0,255)).display();
15425        \endcode
15426        \image html ref_threshold.jpg
15427     **/
15428     CImg<T>& threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) {
15429       if (is_empty()) return *this;
15430       if (strict_threshold) {
15431         if (soft_threshold) cimg_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v+value):(T)0; }
15432         else cimg_for(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0;
15433       } else {
15434         if (soft_threshold) cimg_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v+value):(T)0; }
15435         else cimg_for(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0;
15436       }
15437       return *this;
15438     }
15439 
15440     CImg<T> get_threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) const {
15441       return (+*this).threshold(value,soft_threshold,strict_threshold);
15442     }
15443 
15444     //! Compute the histogram of the instance image.
15445     /**
15446        \param nb_levels Number of desired histogram levels.
15447        \param value_min Minimum pixel value considered for the histogram computation. All pixel values lower than \p value_min will not be counted.
15448        \param value_max Maximum pixel value considered for the histogram computation. All pixel values higher than \p value_max will not be counted.
15449        \return Instance image is replaced by its histogram, defined as a \p CImg<T>(nb_levels) image.
15450        \note
15451        - The histogram H of an image I is the 1d function where H(x) counts the number of occurences of the value x in the image I.
15452        - If \p value_min==value_max==0 (default behavior), the function first estimates the whole range of pixel values
15453        then uses it to compute the histogram.
15454        - The resulting histogram is always defined in 1d. Histograms of multi-valued images are not multi-dimensional.
15455        - Function \p CImg<T>::get_histogram() is also defined. It returns a non-shared modified copy of the instance image.
15456        \par Sample code :
15457        \code
15458        const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
15459        img.display_graph(0,3);
15460        \endcode
15461        \image html ref_histogram.jpg
15462     **/
15463     CImg<T>& histogram(const unsigned int nb_levels, const T value_min=(T)0, const T value_max=(T)0) {
15464       return get_histogram(nb_levels,value_min,value_max).move_to(*this);
15465     }
15466 
15467     CImg<floatT> get_histogram(const unsigned int nb_levels, const T value_min=(T)0, const T value_max=(T)0) const {
15468       if (!nb_levels)
15469         throw CImgArgumentException(_cimg_instance
15470                                     "histogram() : Invalid histogram request with 0 levels.",
15471                                     cimg_instance);
15472 
15473       if (is_empty()) return CImg<floatT>();
15474       T vmin = value_min, vmax = value_max;
15475       CImg<floatT> res(nb_levels,1,1,1,0);
15476       if (vmin>=vmax && vmin==0) vmin = min_max(vmax);
15477       if (vmin<vmax) cimg_for(*this,ptrs,T) {
15478         const T val = *ptrs;
15479         if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels-1:(int)((val-vmin)*nb_levels/(vmax-vmin))];
15480       } else res[0]+=size();
15481       return res;
15482     }
15483 
15484     //! Compute the histogram-equalized version of the instance image.
15485     /**
15486        \param nb_levels Number of histogram levels used for the equalization.
15487        \param value_min Minimum pixel value considered for the histogram computation. All pixel values lower than \p value_min will not be counted.
15488        \param value_max Maximum pixel value considered for the histogram computation. All pixel values higher than \p value_max will not be counted.
15489        \return A reference to the modified instance image.
15490        \note
15491        - If \p value_min==value_max==0 (default behavior), the function first estimates the whole range of pixel values
15492        then uses it to equalize the histogram.
15493        - Function \p CImg<T>::get_equalize() is also defined. It returns a non-shared modified copy of the instance image.
15494        \par Sample code :
15495        \code
15496        const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
15497        (img,res).display();
15498        \endcode
15499        \image html ref_equalize.jpg
15500     **/
15501     CImg<T>& equalize(const unsigned int nb_levels, const T value_min=(T)0, const T value_max=(T)0) {
15502       if (is_empty()) return *this;
15503       T vmin = value_min, vmax = value_max;
15504       if (vmin==vmax && vmin==0) vmin = min_max(vmax);
15505       if (vmin<vmax) {
15506         CImg<floatT> hist = get_histogram(nb_levels,vmin,vmax);
15507         float cumul = 0;
15508         cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
15509         cimg_for(*this,ptrd,T) {
15510           const int pos = (unsigned int)((*ptrd-vmin)*(nb_levels-1)/(vmax-vmin));
15511           if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/size());
15512         }
15513       }
15514       return *this;
15515     }
15516 
15517     CImg<T> get_equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const {
15518       return (+*this).equalize(nblevels,val_min,val_max);
15519     }
15520 
15521     //! Index multi-valued pixels of the instance image, regarding to a predefined palette.
15522     /**
15523        \param palette Multi-valued palette used as the basis for multi-valued pixel indexing.
15524        \param dithering Tells if Floyd-Steinberg dithering is activated or not.
15525        \param map_indexes Tell if the values of the resulting image are the palette indices or the palette vectors.
15526        \return A reference to the modified instance image.
15527        \note
15528        - \p img.index(palette,dithering,1) is equivalent to <tt>img.index(palette,dithering,0).map(palette)</tt>.
15529        - Function \p CImg<T>::get_index() is also defined. It returns a non-shared modified copy of the instance image.
15530        \par Sample code :
15531        \code
15532        const CImg<float> img("reference.jpg"), palette(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
15533        const CImg<float> res = img.get_index(palette,true,true);
15534        (img,res).display();
15535        \endcode
15536        \image html ref_index.jpg
15537     **/
15538     template<typename t>
15539     CImg<T>& index(const CImg<t>& palette, const bool dithering=false, const bool map_indexes=false) {
15540       return get_index(palette,dithering,map_indexes).move_to(*this);
15541     }
15542 
15543     template<typename t>
15544     CImg<typename CImg<t>::Tuint>
15545     get_index(const CImg<t>& palette, const bool dithering=false, const bool map_indexes=true) const {
15546       if (palette._spectrum!=_spectrum)
15547         throw CImgArgumentException(_cimg_instance
15548                                     "index() : Instance and specified palette (%u,%u,%u,%u,%p) "
15549                                     "have incompatible dimensions.",
15550                                     cimg_instance,
15551                                     palette._width,palette._height,palette._depth,palette._spectrum,palette._data);
15552 
15553       typedef typename CImg<t>::Tuint tuint;
15554       if (is_empty()) return CImg<tuint>();
15555       const unsigned int whd = _width*_height*_depth, pwhd = palette._width*palette._height*palette._depth;
15556       CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
15557       tuint *ptrd = res._data;
15558       if (dithering) { // Dithered versions.
15559         Tfloat valm = 0, valM = (Tfloat)max_min(valm);
15560         if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
15561         CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum-1);
15562         Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
15563         const unsigned int cwhd = cache._width*cache._height*cache._depth;
15564         switch (_spectrum) {
15565         case 1 : { // Optimized for scalars.
15566           cimg_forYZ(*this,y,z) {
15567             if (y<height()-2) {
15568               Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y+1,z,0);
15569               cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
15570             }
15571             Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
15572             cimg_forX(*this,x) {
15573               const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
15574               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
15575               for (const t *ptrp0 = palette._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
15576                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
15577                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
15578               }
15579               const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)/16;
15580               *ptrs0+=7*err0; *(ptrsn0-1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
15581               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
15582             }
15583             cimg::swap(cache_current,cache_next);
15584           }
15585         } break;
15586         case 2 : { // Optimized for 2d vectors.
15587           tuint *ptrd1 = ptrd + whd;
15588           cimg_forYZ(*this,y,z) {
15589             if (y<height()-2) {
15590               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
15591               const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd;
15592               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
15593             }
15594             Tfloat
15595               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
15596               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
15597             cimg_forX(*this,x) {
15598               const Tfloat
15599                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
15600                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
15601               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
15602               for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
15603                 const Tfloat
15604                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
15605                   dist = pval0*pval0 + pval1*pval1;
15606                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
15607               }
15608               const t *const ptrmin1 = ptrmin0 + pwhd;
15609               const Tfloat
15610                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)/16,
15611                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)/16;
15612               *ptrs0+=7*err0; *ptrs1+=7*err1;
15613               *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1;
15614               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
15615               *ptrsn0+=err0; *ptrsn1+=err1;
15616               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
15617               else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
15618             }
15619             cimg::swap(cache_current,cache_next);
15620           }
15621         } break;
15622         case 3 : { // Optimized for 3d vectors (colors).
15623           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
15624           cimg_forYZ(*this,y,z) {
15625             if (y<height()-2) {
15626               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
15627               const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
15628               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++); }
15629             }
15630             Tfloat
15631               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
15632               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
15633             cimg_forX(*this,x) {
15634               const Tfloat
15635                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
15636                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
15637                 _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
15638               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
15639               for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
15640                 const Tfloat
15641                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, pval2 = (Tfloat)*(ptrp2++) - val2,
15642                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
15643                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
15644               }
15645               const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
15646               const Tfloat
15647                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)/16,
15648                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)/16,
15649                 err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)/16;
15650               *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
15651               *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1; *(ptrsn2-1)+=3*err2;
15652               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
15653               *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
15654               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2; }
15655               else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
15656             }
15657             cimg::swap(cache_current,cache_next);
15658           }
15659         } break;
15660         default : // Generic version
15661           cimg_forYZ(*this,y,z) {
15662             if (y<height()-2) {
15663               Tfloat *ptrc = cache_next;
15664               cimg_forC(*this,c) {
15665                 Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y+1,z,c);
15666                 cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
15667                 ptrc+=cwhd;
15668               }
15669             }
15670             Tfloat *ptrs = cache_current, *ptrsn = cache_next;
15671             cimg_forX(*this,x) {
15672               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = palette._data;
15673               for (const t *ptrp = palette._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
15674                 Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
15675                 cimg_forC(*this,c) {
15676                   const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
15677                   dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
15678                 }
15679                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
15680               }
15681               const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++)-1;
15682               cimg_forC(*this,c) {
15683                 const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)/16;
15684                 *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
15685                 _ptrmin+=pwhd; _ptrs+=cwhd-1; _ptrsn+=cwhd-2;
15686               }
15687               if (map_indexes) {
15688                 tuint *_ptrd = ptrd++;
15689                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
15690               }
15691               else *(ptrd++) = (tuint)(ptrmin - palette._data);
15692             }
15693             cimg::swap(cache_current,cache_next);
15694           }
15695         }
15696       } else { // Non-dithered versions
15697         switch (_spectrum) {
15698         case 1 : { // Optimized for scalars.
15699           for (const T *ptrs0 = _data, *ptrs_end = ptrs0 + whd; ptrs0<ptrs_end; ) {
15700             const Tfloat val0 = (Tfloat)*(ptrs0++);
15701             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
15702             for (const t *ptrp0 = palette._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
15703               const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
15704               if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
15705             }
15706             if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
15707           }
15708         } break;
15709         case 2 : { // Optimized for 2d vectors.
15710           tuint *ptrd1 = ptrd + whd;
15711           for (const T *ptrs0 = _data, *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs1; ptrs0<ptrs_end; ) {
15712             const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
15713             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
15714             for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
15715               const Tfloat
15716                 pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
15717                 dist = pval0*pval0 + pval1*pval1;
15718               if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
15719             }
15720             if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
15721             else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
15722           }
15723         } break;
15724         case 3 : { // Optimized for 3d vectors (colors).
15725           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
15726           for (const T *ptrs0 = _data, *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd, *ptrs_end = ptrs1; ptrs0<ptrs_end; ) {
15727             const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
15728             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = palette._data;
15729             for (const t *ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
15730               const Tfloat
15731                 pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, pval2 = (Tfloat)*(ptrp2++) - val2,
15732                 dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
15733               if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
15734             }
15735             if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd); }
15736             else *(ptrd++) = (tuint)(ptrmin0 - palette._data);
15737           }
15738         } break;
15739         default : // Generic version.
15740           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ++ptrs) {
15741             Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = palette._data;
15742             for (const t *ptrp = palette._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
15743               Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
15744               cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
15745               if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
15746             }
15747             if (map_indexes) {
15748               tuint *_ptrd = ptrd++;
15749               cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
15750             }
15751             else *(ptrd++) = (tuint)(ptrmin - palette._data);
15752           }
15753         }
15754       }
15755       return res;
15756     }
15757 
15758     //! Map predefined palette on the scalar (indexed) instance image.
15759     /**
15760        \param palette Multi-valued palette used for mapping the indexes.
15761        \return A reference to the modified instance image.
15762        \note
15763        - Function \p CImg<T>::get_map() is also defined. It returns a non-shared modified copy of the instance image.
15764        \par Sample code :
15765        \code
15766        const CImg<float> img("reference.jpg"),
15767                          palette1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
15768                          palette2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
15769                          res = img.get_index(palette1,false).map(palette2);
15770        (img,res).display();
15771        \endcode
15772        \image html ref_map.jpg
15773     **/
15774     template<typename t>
15775     CImg<T>& map(const CImg<t>& palette) {
15776       return get_map(palette).move_to(*this);
15777     }
15778 
15779     template<typename t>
15780     CImg<t> get_map(const CImg<t>& palette) const {
15781       if (_spectrum!=1 && palette._spectrum!=1)
15782         throw CImgArgumentException(_cimg_instance
15783                                     "map() : Instance and specified palette (%u,%u,%u,%u,%p) "
15784                                     "have incompatible dimensions.",
15785                                     cimg_instance,
15786                                     palette._width,palette._height,palette._depth,palette._spectrum,palette._data);
15787 
15788       const unsigned int whd = _width*_height*_depth, pwhd = palette._width*palette._height*palette._depth;
15789       CImg<t> res(_width,_height,_depth,palette._spectrum==1?_spectrum:palette._spectrum);
15790       switch (palette._spectrum) {
15791       case 1 : { // Optimized for scalars.
15792         const T *ptrs = _data + whd*_spectrum;
15793         cimg_for(res,ptrd,t) {
15794           const unsigned int _ind = (unsigned int)*(--ptrs), ind = _ind<pwhd?_ind:0;
15795           *ptrd = palette[ind];
15796         }
15797       } break;
15798       case 2 : { // Optimized for 2d vectors.
15799         const t *const ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd;
15800         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
15801         for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
15802           const unsigned int _ind = (unsigned int)*(ptrs++), ind = _ind<pwhd?_ind:0;
15803           *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
15804         }
15805       } break;
15806       case 3 : { // Optimized for 3d vectors (colors).
15807         const t *const ptrp0 = palette._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd;
15808         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
15809         for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
15810           const unsigned int _ind = (unsigned int)*(ptrs++), ind = _ind<pwhd?_ind:0;
15811           *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
15812         }
15813       } break;
15814       default : { // Generic version.
15815         t *ptrd = res._data;
15816         for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
15817           const unsigned int _ind = (unsigned int)*(ptrs++), ind = _ind<pwhd?_ind:0;
15818           const t *ptrp = palette._data + ind;
15819           t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=pwhd; }
15820         }
15821       }
15822       }
15823       return res;
15824     }
15825 
15826     //! Create a map of indexed labels counting disconnected regions with same intensities.
15827     /**
15828        \return A reference to the modified instance image.
15829        \note
15830        - Function \p CImg<T>::get_label_regions() is also defined. It returns a non-shared modified copy of the instance image.
15831        \par Sample code :
15832        \code
15833        const CImg<float> img = CImg<float>("reference.jpg").norm().quantize(4),
15834                          palette = CImg<float>::default_LUT256(),
15835                          res = img.get_label_regions().normalize(0,255).map(palette);
15836        (img,res).display();
15837        \endcode
15838        \image html ref_label_regions.jpg
15839     **/
15840     CImg<T>& label_regions() {
15841       return get_label_regions().move_to(*this);
15842     }
15843 
15844     CImg<uintT> get_label_regions() const {
15845 #define _cimg_get_label_test(p,q) { \
15846   flag = true; \
15847   const T *ptr1 = data(x,y) + siz, *ptr2 = data(p,q) + siz; \
15848   for (unsigned int i = _spectrum; flag && i; --i) { ptr1-=wh; ptr2-=wh; flag = (*ptr1==*ptr2); } \
15849 }
15850       if (_depth>1)
15851         throw CImgInstanceException(_cimg_instance
15852                                     "label_regions() : Instance is not a 2d image.",
15853                                     cimg_instance);
15854 
15855       CImg<uintT> res(_width,_height,_depth,1,0);
15856       unsigned int label = 1;
15857       const unsigned int wh = _width*_height, siz = _width*_height*_spectrum;
15858       const int W1 = width()-1, H1 = height()-1;
15859       bool flag;
15860       cimg_forXY(*this,x,y) {
15861         bool done = false;
15862         if (y) {
15863           _cimg_get_label_test(x,y-1);
15864           if (flag) {
15865             const unsigned int lab = (res(x,y) = res(x,y-1));
15866             done = true;
15867             if (x && res(x-1,y)!=lab) {
15868               _cimg_get_label_test(x-1,y);
15869               if (flag) {
15870                 const unsigned int lold = res(x-1,y), *const cptr = res.data(x,y);
15871                 for (unsigned int *ptr = res._data; ptr<cptr; ++ptr) if (*ptr==lold) *ptr = lab;
15872               }
15873             }
15874           }
15875         }
15876         if (x && !done) {
15877           _cimg_get_label_test(x-1,y);
15878           if (flag) { res(x,y) = res(x-1,y); done = true; }
15879         }
15880         if (!done) res(x,y) = label++;
15881       }
15882       for (int y = H1; y>=0; --y) for (int x=W1; x>=0; --x) {
15883         bool done = false;
15884         if (y<H1) {
15885           _cimg_get_label_test(x,y+1);
15886           if (flag) {
15887             const unsigned int lab = (res(x,y) = res(x,y+1));
15888             done = true;
15889             if (x<W1 && res(x+1,y)!=lab) {
15890               _cimg_get_label_test(x+1,y);
15891               if (flag) {
15892                 const unsigned int lold = res(x+1,y), *const cptr = res.data(x,y);
15893                 for (unsigned int *ptr = res._data+res.size()-1; ptr>cptr; --ptr) if (*ptr==lold) *ptr = lab;
15894               }
15895             }
15896           }
15897         }
15898         if (x<W1 && !done) { _cimg_get_label_test(x+1,y); if (flag) res(x,y) = res(x+1,y); done = true; }
15899       }
15900       const unsigned int lab0 = res.max()+1;
15901       label = lab0;
15902       cimg_foroff(res,off) { // Relabel regions
15903         const unsigned int lab = res[off];
15904         if (lab<lab0) { cimg_for(res,ptrd,unsigned int) if (*ptrd==lab) *ptrd = label; ++label; }
15905       }
15906       return (res-=lab0);
15907     }
15908 
15909     //@}
15910     //---------------------------------
15911     //
15912     //! \name Color Base Management
15913     //@{
15914     //---------------------------------
15915 
15916     //! Return a default indexed color palette with 256 (R,G,B) entries.
15917     /**
15918        \return An instance of a default color palette with 256 (R,G,B) colors.
15919        \note This color palette is particularly used by %CImg when displaying images on 256 colors displays.
15920              It consists in the quantification of the (R,G,B) color space using 3:3:2 bits for color coding
15921              (i.e 8 levels for the Red and Green and 4 levels for the Blue).
15922        \code
15923        CImg<float>::default_LUT256().display();
15924        \endcode
15925        \image html ref_default_LUT256.jpg
15926     **/
15927     static CImg<Tuchar> default_LUT256() {
15928       static CImg<Tuchar> palette;
15929       if (!palette) {
15930         palette.assign(1,256,1,3);
15931         for (unsigned int index = 0, r = 16; r<256; r+=32)
15932           for (unsigned int g = 16; g<256; g+=32)
15933             for (unsigned int b = 32; b<256; b+=64) {
15934               palette(0,index,0) = (Tuchar)r;
15935               palette(0,index,1) = (Tuchar)g;
15936               palette(0,index++,2) = (Tuchar)b;
15937             }
15938       }
15939       return palette;
15940     }
15941 
15942     //! Return a rainbow indexed color palette with 256 (R,G,B) entries.
15943     /**
15944        \return An instance of a rainbow color palette with 256 (R,G,B) colors.
15945        \code
15946        CImg<float>::rainbow_LUT256().display();
15947        \endcode
15948        \image html ref_rainbow_LUT256.jpg
15949     **/
15950     static CImg<Tuchar> rainbow_LUT256() {
15951       static CImg<Tuchar> palette;
15952       if (!palette) {
15953         CImg<Tint> tmp(1,256,1,3,1);
15954         tmp.get_shared_channel(0).sequence(0,359);
15955         palette = tmp.HSVtoRGB();
15956       }
15957       return palette;
15958     }
15959 
15960     //! Return a contrasted indexed color palette with 256 (R,G,B) entries.
15961     /**
15962        \return An instance of a contrasted color palette with 256 (R,G,B) colors.
15963        \code
15964        CImg<float>::contrast_LUT256().display();
15965        \endcode
15966        \image html ref_contrast_LUT256.jpg
15967     **/
15968     static CImg<Tuchar> contrast_LUT256() {
15969       static const unsigned char pal[] = {
15970         217,62,88,75,1,237,240,12,56,160,165,116,1,1,204,2,15,248,148,185,133,141,46,246,222,116,16,5,207,226,
15971         17,114,247,1,214,53,238,0,95,55,233,235,109,0,17,54,33,0,90,30,3,0,94,27,19,0,68,212,166,130,0,15,7,119,
15972         238,2,246,198,0,3,16,10,13,2,25,28,12,6,2,99,18,141,30,4,3,140,12,4,30,233,7,10,0,136,35,160,168,184,20,
15973         233,0,1,242,83,90,56,180,44,41,0,6,19,207,5,31,214,4,35,153,180,75,21,76,16,202,218,22,17,2,136,71,74,
15974         81,251,244,148,222,17,0,234,24,0,200,16,239,15,225,102,230,186,58,230,110,12,0,7,129,249,22,241,37,219,
15975         1,3,254,210,3,212,113,131,197,162,123,252,90,96,209,60,0,17,0,180,249,12,112,165,43,27,229,77,40,195,12,
15976         87,1,210,148,47,80,5,9,1,137,2,40,57,205,244,40,8,252,98,0,40,43,206,31,187,0,180,1,69,70,227,131,108,0,
15977         223,94,228,35,248,243,4,16,0,34,24,2,9,35,73,91,12,199,51,1,249,12,103,131,20,224,2,70,32,
15978         233,1,165,3,8,154,246,233,196,5,0,6,183,227,247,195,208,36,0,0,226,160,210,198,69,153,210,1,23,8,192,2,4,
15979         137,1,0,52,2,249,241,129,0,0,234,7,238,71,7,32,15,157,157,252,158,2,250,6,13,30,11,162,0,199,21,11,27,224,
15980         4,157,20,181,111,187,218,3,0,11,158,230,196,34,223,22,248,135,254,210,157,219,0,117,239,3,255,4,227,5,247,
15981         11,4,3,188,111,11,105,195,2,0,14,1,21,219,192,0,183,191,113,241,1,12,17,248,0,48,7,19,1,254,212,0,239,246,
15982         0,23,0,250,165,194,194,17,3,253,0,24,6,0,141,167,221,24,212,2,235,243,0,0,205,1,251,133,204,28,4,6,1,10,
15983         141,21,74,12,236,254,228,19,1,0,214,1,186,13,13,6,13,16,27,209,6,216,11,207,251,59,32,9,155,23,19,235,143,
15984         116,6,213,6,75,159,23,6,0,228,4,10,245,249,1,7,44,234,4,102,174,0,19,239,103,16,15,18,8,214,22,4,47,244,
15985         255,8,0,251,173,1,212,252,250,251,252,6,0,29,29,222,233,246,5,149,0,182,180,13,151,0,203,183,0,35,149,0,
15986         235,246,254,78,9,17,203,73,11,195,0,3,5,44,0,0,237,5,106,6,130,16,214,20,168,247,168,4,207,11,5,1,232,251,
15987         129,210,116,231,217,223,214,27,45,38,4,177,186,249,7,215,172,16,214,27,249,230,236,2,34,216,217,0,175,30,
15988         243,225,244,182,20,212,2,226,21,255,20,0,2,13,62,13,191,14,76,64,20,121,4,118,0,216,1,147,0,2,210,1,215,
15989         95,210,236,225,184,46,0,248,24,11,1,9,141,250,243,9,221,233,160,11,147,2,55,8,23,12,253,9,0,54,0,231,6,3,
15990         141,8,2,246,9,180,5,11,8,227,8,43,110,242,1,130,5,97,36,10,6,219,86,133,11,108,6,1,5,244,67,19,28,0,174,
15991         154,16,127,149,252,188,196,196,228,244,9,249,0,0,0,37,170,32,250,0,73,255,23,3,224,234,38,195,198,0,255,87,
15992         33,221,174,31,3,0,189,228,6,153,14,144,14,108,197,0,9,206,245,254,3,16,253,178,248,0,95,125,8,0,3,168,21,
15993         23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 };
15994       static const CImg<Tuchar> palette(pal,1,256,1,3,false);
15995       return palette;
15996     }
15997 
15998     //! Convert color pixels from (R,G,B) to (H,S,V).
15999     CImg<T>& RGBtoHSV() {
16000       if (_spectrum!=3)
16001         throw CImgInstanceException(_cimg_instance
16002                                     "RGBtoHSV() : Instance is not a RGB image.",
16003                                     cimg_instance);
16004 
16005       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16006       for (unsigned int N = _width*_height*_depth; N; --N) {
16007         const Tfloat
16008           R = (Tfloat)*p1,
16009           G = (Tfloat)*p2,
16010           B = (Tfloat)*p3,
16011           nR = (R<0?0:(R>255?255:R))/255,
16012           nG = (G<0?0:(G>255?255:G))/255,
16013           nB = (B<0?0:(B>255?255:B))/255,
16014           m = cimg::min(nR,nG,nB),
16015           M = cimg::max(nR,nG,nB);
16016         Tfloat H = 0, S = 0;
16017         if (M!=m) {
16018           const Tfloat
16019             f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
16020             i = (Tfloat)((nR==m)?3:((nG==m)?5:1));
16021           H = (i-f/(M-m));
16022           if (H>=6) H-=6;
16023           H*=60;
16024           S = (M-m)/M;
16025         }
16026         *(p1++) = (T)H;
16027         *(p2++) = (T)S;
16028         *(p3++) = (T)M;
16029       }
16030       return *this;
16031     }
16032 
16033     CImg<Tfloat> get_RGBtoHSV() const {
16034       return CImg<Tfloat>(*this,false).RGBtoHSV();
16035     }
16036 
16037     //! Convert color pixels from (H,S,V) to (R,G,B).
16038     CImg<T>& HSVtoRGB() {
16039       if (_spectrum!=3)
16040         throw CImgInstanceException(_cimg_instance
16041                                     "HSVtoRGB() : Instance is not a HSV image.",
16042                                     cimg_instance);
16043 
16044       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16045       for (unsigned int N = _width*_height*_depth; N; --N) {
16046         Tfloat
16047           H = (Tfloat)*p1,
16048           S = (Tfloat)*p2,
16049           V = (Tfloat)*p3,
16050           R = 0, G = 0, B = 0;
16051         if (H==0 && S==0) R = G = B = V;
16052         else {
16053           H/=60;
16054           const int i = (int)std::floor(H);
16055           const Tfloat
16056             f = (i&1)?(H - i):(1 - H + i),
16057             m = V*(1 - S),
16058             n = V*(1 - S*f);
16059           switch (i) {
16060           case 6 :
16061           case 0 : R = V; G = n; B = m; break;
16062           case 1 : R = n; G = V; B = m; break;
16063           case 2 : R = m; G = V; B = n; break;
16064           case 3 : R = m; G = n; B = V; break;
16065           case 4 : R = n; G = m; B = V; break;
16066           case 5 : R = V; G = m; B = n; break;
16067           }
16068         }
16069         R*=255; G*=255; B*=255;
16070         *(p1++) = (T)(R<0?0:(R>255?255:R));
16071         *(p2++) = (T)(G<0?0:(G>255?255:G));
16072         *(p3++) = (T)(B<0?0:(B>255?255:B));
16073       }
16074       return *this;
16075     }
16076 
16077     CImg<Tuchar> get_HSVtoRGB() const {
16078       return CImg<Tuchar>(*this,false).HSVtoRGB();
16079     }
16080 
16081     //! Convert color pixels from (R,G,B) to (H,S,L).
16082     CImg<T>& RGBtoHSL() {
16083       if (_spectrum!=3)
16084         throw CImgInstanceException(_cimg_instance
16085                                     "RGBtoHSL() : Instance is not a RGB image.",
16086                                     cimg_instance);
16087 
16088       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16089       for (unsigned int N = _width*_height*_depth; N; --N) {
16090         const Tfloat
16091           R = (Tfloat)*p1,
16092           G = (Tfloat)*p2,
16093           B = (Tfloat)*p3,
16094           nR = (R<0?0:(R>255?255:R))/255,
16095           nG = (G<0?0:(G>255?255:G))/255,
16096           nB = (B<0?0:(B>255?255:B))/255,
16097           m = cimg::min(nR,nG,nB),
16098           M = cimg::max(nR,nG,nB),
16099           L = (m + M)/2;
16100         Tfloat H = 0, S = 0;
16101         if (M==m) H = S = 0;
16102         else {
16103           const Tfloat
16104             f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
16105             i = (nR==m)?3.0f:((nG==m)?5.0f:1.0f);
16106           H = (i-f/(M-m));
16107           if (H>=6) H-=6;
16108           H*=60;
16109           S = (2*L<=1)?((M-m)/(M+m)):((M-m)/(2-M-m));
16110         }
16111         *(p1++) = (T)H;
16112         *(p2++) = (T)S;
16113         *(p3++) = (T)L;
16114       }
16115       return *this;
16116     }
16117 
16118     CImg<Tfloat> get_RGBtoHSL() const {
16119       return CImg< Tfloat>(*this,false).RGBtoHSL();
16120     }
16121 
16122     //! Convert color pixels from (H,S,L) to (R,G,B).
16123     CImg<T>& HSLtoRGB() {
16124       if (_spectrum!=3)
16125         throw CImgInstanceException(_cimg_instance
16126                                     "HSLtoRGB() : Instance is not a HSL image.",
16127                                     cimg_instance);
16128 
16129       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16130       for (unsigned int N = _width*_height*_depth; N; --N) {
16131         const Tfloat
16132           H = (Tfloat)*p1,
16133           S = (Tfloat)*p2,
16134           L = (Tfloat)*p3,
16135           q = 2*L<1?L*(1+S):(L+S-L*S),
16136           p = 2*L-q,
16137           h = H/360,
16138           tr = h + 1.0f/3,
16139           tg = h,
16140           tb = h - 1.0f/3,
16141           ntr = tr<0?tr+1:(tr>1?tr-1:tr),
16142           ntg = tg<0?tg+1:(tg>1?tg-1:tg),
16143           ntb = tb<0?tb+1:(tb>1?tb-1:tb),
16144           R = 255*(6*ntr<1?p+(q-p)*6*ntr:(2*ntr<1?q:(3*ntr<2?p+(q-p)*6*(2.0f/3-ntr):p))),
16145           G = 255*(6*ntg<1?p+(q-p)*6*ntg:(2*ntg<1?q:(3*ntg<2?p+(q-p)*6*(2.0f/3-ntg):p))),
16146           B = 255*(6*ntb<1?p+(q-p)*6*ntb:(2*ntb<1?q:(3*ntb<2?p+(q-p)*6*(2.0f/3-ntb):p)));
16147         *(p1++) = (T)(R<0?0:(R>255?255:R));
16148         *(p2++) = (T)(G<0?0:(G>255?255:G));
16149         *(p3++) = (T)(B<0?0:(B>255?255:B));
16150       }
16151       return *this;
16152     }
16153 
16154     CImg<Tuchar> get_HSLtoRGB() const {
16155       return CImg<Tuchar>(*this,false).HSLtoRGB();
16156     }
16157 
16158     //! Convert color pixels from (R,G,B) to (H,S,I).
16159     //! Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002.
16160     CImg<T>& RGBtoHSI() {
16161       if (_spectrum!=3)
16162         throw CImgInstanceException(_cimg_instance
16163                                     "RGBtoHSI() : Instance is not a RGB image.",
16164                                     cimg_instance);
16165 
16166       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16167       for (unsigned int N = _width*_height*_depth; N; --N) {
16168         const Tfloat
16169           R = (Tfloat)*p1,
16170           G = (Tfloat)*p2,
16171           B = (Tfloat)*p3,
16172           nR = (R<0?0:(R>255?255:R))/255,
16173           nG = (G<0?0:(G>255?255:G))/255,
16174           nB = (B<0?0:(B>255?255:B))/255,
16175           m = cimg::min(nR,nG,nB),
16176           theta = (Tfloat)(std::acos(0.5f*((nR-nG)+(nR-nB))/std::sqrt(std::pow(nR-nG,2)+(nR-nB)*(nG-nB)))*180/cimg::PI),
16177           sum = nR + nG + nB;
16178         Tfloat H = 0, S = 0, I = 0;
16179         if (theta>0) H = (nB<=nG)?theta:360-theta;
16180         if (sum>0) S = 1 - 3/sum*m;
16181         I = sum/3;
16182         *(p1++) = (T)H;
16183         *(p2++) = (T)S;
16184         *(p3++) = (T)I;
16185       }
16186       return *this;
16187     }
16188 
16189     CImg<Tfloat> get_RGBtoHSI() const {
16190       return CImg<Tfloat>(*this,false).RGBtoHSI();
16191     }
16192 
16193     //! Convert color pixels from (H,S,I) to (R,G,B).
16194     CImg<T>& HSItoRGB() {
16195       if (_spectrum!=3)
16196         throw CImgInstanceException(_cimg_instance
16197                                     "HSItoRGB() : Instance is not a HSI image.",
16198                                     cimg_instance);
16199 
16200       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16201       for (unsigned int N = _width*_height*_depth; N; --N) {
16202         Tfloat
16203           H = (Tfloat)*p1,
16204           S = (Tfloat)*p2,
16205           I = (Tfloat)*p3,
16206           a = I*(1-S),
16207           R = 0, G = 0, B = 0;
16208         if (H<120) {
16209           B = a;
16210           R = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
16211           G = 3*I-(R+B);
16212         } else if (H<240) {
16213           H-=120;
16214           R = a;
16215           G = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
16216           B = 3*I-(R+G);
16217         } else {
16218           H-=240;
16219           G = a;
16220           B = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
16221           R = 3*I-(G+B);
16222         }
16223         R*=255; G*=255; B*=255;
16224         *(p1++) = (T)(R<0?0:(R>255?255:R));
16225         *(p2++) = (T)(G<0?0:(G>255?255:G));
16226         *(p3++) = (T)(B<0?0:(B>255?255:B));
16227       }
16228       return *this;
16229     }
16230 
16231     CImg<Tfloat> get_HSItoRGB() const {
16232       return CImg< Tuchar>(*this,false).HSItoRGB();
16233     }
16234 
16235     //! Convert color pixels from (R,G,B) to (Y,Cb,Cr).
16236     CImg<T>& RGBtoYCbCr() {
16237       if (_spectrum!=3)
16238         throw CImgInstanceException(_cimg_instance
16239                                     "RGBtoYCbCr() : Instance is not a RGB image.",
16240                                     cimg_instance);
16241 
16242       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16243       for (unsigned int N = _width*_height*_depth; N; --N) {
16244         const Tfloat
16245           R = (Tfloat)*p1,
16246           G = (Tfloat)*p2,
16247           B = (Tfloat)*p3,
16248           Y = (66*R + 129*G + 25*B + 128)/256 + 16,
16249           Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
16250           Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
16251         *(p1++) = (T)(Y<0?0:(Y>255?255:Y));
16252         *(p2++) = (T)(Cb<0?0:(Cb>255?255:Cb));
16253         *(p3++) = (T)(Cr<0?0:(Cr>255?255:Cr));
16254       }
16255       return *this;
16256     }
16257 
16258     CImg<Tuchar> get_RGBtoYCbCr() const {
16259       return CImg<Tuchar>(*this,false).RGBtoYCbCr();
16260     }
16261 
16262     //! Convert color pixels from (R,G,B) to (Y,Cb,Cr).
16263     CImg<T>& YCbCrtoRGB() {
16264       if (_spectrum!=3)
16265         throw CImgInstanceException(_cimg_instance
16266                                     "YCbCrtoRGB() : Instance is not a YCbCr image.",
16267                                     cimg_instance);
16268 
16269       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16270       for (unsigned int N = _width*_height*_depth; N; --N) {
16271         const Tfloat
16272           Y = (Tfloat)*p1 - 16,
16273           Cb = (Tfloat)*p2 - 128,
16274           Cr = (Tfloat)*p3 - 128,
16275           R = (298*Y + 409*Cr + 128)/256,
16276           G = (298*Y - 100*Cb - 208*Cr + 128)/256,
16277           B = (298*Y + 516*Cb + 128)/256;
16278         *(p1++) = (T)(R<0?0:(R>255?255:R));
16279         *(p2++) = (T)(G<0?0:(G>255?255:G));
16280         *(p3++) = (T)(B<0?0:(B>255?255:B));
16281       }
16282       return *this;
16283     }
16284 
16285     CImg<Tuchar> get_YCbCrtoRGB() const {
16286       return CImg<Tuchar>(*this,false).YCbCrtoRGB();
16287     }
16288 
16289     //! Convert color pixels from (R,G,B) to (Y,U,V).
16290     CImg<T>& RGBtoYUV() {
16291       if (_spectrum!=3)
16292         throw CImgInstanceException(_cimg_instance
16293                                     "RGBtoYUV() : Instance is not a RGB image.",
16294                                     cimg_instance);
16295 
16296       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16297       for (unsigned int N = _width*_height*_depth; N; --N) {
16298         const Tfloat
16299           R = (Tfloat)*p1/255,
16300           G = (Tfloat)*p2/255,
16301           B = (Tfloat)*p3/255,
16302           Y = 0.299f*R + 0.587f*G + 0.114f*B;
16303         *(p1++) = (T)Y;
16304         *(p2++) = (T)(0.492f*(B-Y));
16305         *(p3++) = (T)(0.877*(R-Y));
16306       }
16307       return *this;
16308     }
16309 
16310     CImg<Tfloat> get_RGBtoYUV() const {
16311       return CImg<Tfloat>(*this,false).RGBtoYUV();
16312     }
16313 
16314     //! Convert color pixels from (Y,U,V) to (R,G,B).
16315     CImg<T>& YUVtoRGB() {
16316       if (_spectrum!=3)
16317         throw CImgInstanceException(_cimg_instance
16318                                     "YUVtoRGB() : Instance is not a YUV image.",
16319                                     cimg_instance);
16320 
16321       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16322       for (unsigned int N = _width*_height*_depth; N; --N) {
16323         const Tfloat
16324           Y = (Tfloat)*p1,
16325           U = (Tfloat)*p2,
16326           V = (Tfloat)*p3,
16327           R = (Y + 1.140f*V)*255,
16328           G = (Y - 0.395f*U - 0.581f*V)*255,
16329           B = (Y + 2.032f*U)*255;
16330         *(p1++) = (T)(R<0?0:(R>255?255:R));
16331         *(p2++) = (T)(G<0?0:(G>255?255:G));
16332         *(p3++) = (T)(B<0?0:(B>255?255:B));
16333       }
16334       return *this;
16335     }
16336 
16337     CImg<Tuchar> get_YUVtoRGB() const {
16338       return CImg< Tuchar>(*this,false).YUVtoRGB();
16339     }
16340 
16341     //! Convert color pixels from (R,G,B) to (C,M,Y).
16342     CImg<T>& RGBtoCMY() {
16343       if (_spectrum!=3)
16344         throw CImgInstanceException(_cimg_instance
16345                                     "RGBtoCMY() : Instance is not a RGB image.",
16346                                     cimg_instance);
16347 
16348       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16349       for (unsigned int N = _width*_height*_depth; N; --N) {
16350         const Tfloat
16351           R = (Tfloat)*p1,
16352           G = (Tfloat)*p2,
16353           B = (Tfloat)*p3,
16354           C = 255 - R,
16355           M = 255 - G,
16356           Y = 255 - B;
16357         *(p1++) = (T)(C<0?0:(C>255?255:C));
16358         *(p2++) = (T)(M<0?0:(M>255?255:M));
16359         *(p3++) = (T)(Y<0?0:(Y>255?255:Y));
16360       }
16361       return *this;
16362     }
16363 
16364     CImg<Tuchar> get_RGBtoCMY() const {
16365       return CImg<Tfloat>(*this,false).RGBtoCMY();
16366     }
16367 
16368     //! Convert (C,M,Y) pixels of a color image into the (R,G,B) color space.
16369     CImg<T>& CMYtoRGB() {
16370       if (_spectrum!=3)
16371         throw CImgInstanceException(_cimg_instance
16372                                     "CMYtoRGB() : Instance is not a CMY image.",
16373                                     cimg_instance);
16374 
16375       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16376       for (unsigned int N = _width*_height*_depth; N; --N) {
16377         const Tfloat
16378           C = (Tfloat)*p1,
16379           M = (Tfloat)*p2,
16380           Y = (Tfloat)*p3,
16381           R = 255 - C,
16382           G = 255 - M,
16383           B = 255 - Y;
16384         *(p1++) = (T)(R<0?0:(R>255?255:R));
16385         *(p2++) = (T)(G<0?0:(G>255?255:G));
16386         *(p3++) = (T)(B<0?0:(B>255?255:B));
16387       }
16388       return *this;
16389     }
16390 
16391     CImg<Tuchar> get_CMYtoRGB() const {
16392       return CImg<Tuchar>(*this,false).CMYtoRGB();
16393     }
16394 
16395     //! Convert color pixels from (C,M,Y) to (C,M,Y,K).
16396     CImg<T>& CMYtoCMYK() {
16397       return get_CMYtoCMYK().move_to(*this);
16398     }
16399 
16400     CImg<Tuchar> get_CMYtoCMYK() const {
16401       if (_spectrum!=3)
16402         throw CImgInstanceException(_cimg_instance
16403                                     "CMYtoCMYK() : Instance is not a CMY image.",
16404                                     cimg_instance);
16405 
16406       CImg<Tfloat> res(_width,_height,_depth,4);
16407       const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
16408       Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2), *pd4 = res.data(0,0,0,3);
16409       for (unsigned int N = _width*_height*_depth; N; --N) {
16410         Tfloat
16411           C = (Tfloat)*(ps1++),
16412           M = (Tfloat)*(ps2++),
16413           Y = (Tfloat)*(ps3++),
16414           K = cimg::min(C,M,Y);
16415         if (K>=255) C = M = Y = 0;
16416         else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
16417         *(pd1++) = (Tfloat)(C<0?0:(C>255?255:C));
16418         *(pd2++) = (Tfloat)(M<0?0:(M>255?255:M));
16419         *(pd3++) = (Tfloat)(Y<0?0:(Y>255?255:Y));
16420         *(pd4++) = (Tfloat)(K<0?0:(K>255?255:K));
16421       }
16422       return res;
16423     }
16424 
16425     //! Convert (C,M,Y,K) pixels of a color image into the (C,M,Y) color space.
16426     CImg<T>& CMYKtoCMY() {
16427       return get_CMYKtoCMY().move_to(*this);
16428     }
16429 
16430     CImg<Tfloat> get_CMYKtoCMY() const {
16431       if (_spectrum!=4)
16432         throw CImgInstanceException(_cimg_instance
16433                                     "CMYKtoCMY() : Instance is not a CMYK image.",
16434                                     cimg_instance);
16435 
16436       CImg<Tfloat> res(_width,_height,_depth,3);
16437       const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2), *ps4 = data(0,0,0,3);
16438       Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
16439       for (unsigned int N = _width*_height*_depth; N; --N) {
16440         const Tfloat
16441           C = (Tfloat)*(ps1++),
16442           M = (Tfloat)*(ps2++),
16443           Y = (Tfloat)*(ps3++),
16444           K = (Tfloat)*(ps4++),
16445           K1 = 1 - K/255,
16446           nC = C*K1 + K,
16447           nM = M*K1 + K,
16448           nY = Y*K1 + K;
16449         *(pd1++) = (Tfloat)(nC<0?0:(nC>255?255:nC));
16450         *(pd2++) = (Tfloat)(nM<0?0:(nM>255?255:nM));
16451         *(pd3++) = (Tfloat)(nY<0?0:(nY>255?255:nY));
16452       }
16453       return res;
16454     }
16455 
16456     //! Convert color pixels from (R,G,B) to (X,Y,Z)_709.
16457     CImg<T>& RGBtoXYZ() {
16458       if (_spectrum!=3)
16459         throw CImgInstanceException(_cimg_instance
16460                                     "RGBtoXYZ() : Instance is not a RGB image.",
16461                                     cimg_instance);
16462 
16463       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16464       for (unsigned int N = _width*_height*_depth; N; --N) {
16465         const Tfloat
16466           R = (Tfloat)*p1/255,
16467           G = (Tfloat)*p2/255,
16468           B = (Tfloat)*p3/255;
16469         *(p1++) = (T)(0.412453f*R + 0.357580f*G + 0.180423f*B);
16470         *(p2++) = (T)(0.212671f*R + 0.715160f*G + 0.072169f*B);
16471         *(p3++) = (T)(0.019334f*R + 0.119193f*G + 0.950227f*B);
16472       }
16473       return *this;
16474     }
16475 
16476     CImg<Tfloat> get_RGBtoXYZ() const {
16477       return CImg<Tfloat>(*this,false).RGBtoXYZ();
16478     }
16479 
16480     //! Convert (X,Y,Z)_709 pixels of a color image into the (R,G,B) color space.
16481     CImg<T>& XYZtoRGB() {
16482       if (_spectrum!=3)
16483         throw CImgInstanceException(_cimg_instance
16484                                     "XYZtoRGB() : Instance is not a XYZ image.",
16485                                     cimg_instance);
16486 
16487       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16488       for (unsigned int N = _width*_height*_depth; N; --N) {
16489         const Tfloat
16490           X = (Tfloat)*p1*255,
16491           Y = (Tfloat)*p2*255,
16492           Z = (Tfloat)*p3*255,
16493           R = 3.240479f*X  - 1.537150f*Y - 0.498535f*Z,
16494           G = -0.969256f*X + 1.875992f*Y + 0.041556f*Z,
16495           B = 0.055648f*X  - 0.204043f*Y + 1.057311f*Z;
16496         *(p1++) = (T)(R<0?0:(R>255?255:R));
16497         *(p2++) = (T)(G<0?0:(G>255?255:G));
16498         *(p3++) = (T)(B<0?0:(B>255?255:B));
16499       }
16500       return *this;
16501     }
16502 
16503     CImg<Tuchar> get_XYZtoRGB() const {
16504       return CImg<Tuchar>(*this,false).XYZtoRGB();
16505     }
16506 
16507     //! Convert (X,Y,Z)_709 pixels of a color image into the (L*,a*,b*) color space.
16508     CImg<T>& XYZtoLab() {
16509 #define _cimg_Labf(x) ((x)>=0.008856f?(std::pow(x,(Tfloat)1/3)):(7.787f*(x)+16.0f/116))
16510 
16511       if (_spectrum!=3)
16512         throw CImgInstanceException(_cimg_instance
16513                                     "XYZtoLab() : Instance is not a XYZ image.",
16514                                     cimg_instance);
16515 
16516       const Tfloat
16517         Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
16518         Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
16519         Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
16520       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16521       for (unsigned int N = _width*_height*_depth; N; --N) {
16522         const Tfloat
16523           X = (Tfloat)*p1,
16524           Y = (Tfloat)*p2,
16525           Z = (Tfloat)*p3,
16526           XXn = X/Xn, YYn = Y/Yn, ZZn = Z/Zn,
16527           fX = (Tfloat)_cimg_Labf(XXn),
16528           fY = (Tfloat)_cimg_Labf(YYn),
16529           fZ = (Tfloat)_cimg_Labf(ZZn);
16530         *(p1++) = (T)(116*fY - 16);
16531         *(p2++) = (T)(500*(fX - fY));
16532         *(p3++) = (T)(200*(fY - fZ));
16533       }
16534       return *this;
16535     }
16536 
16537     CImg<Tfloat> get_XYZtoLab() const {
16538       return CImg<Tfloat>(*this,false).XYZtoLab();
16539     }
16540 
16541     //! Convert (L,a,b) pixels of a color image into the (X,Y,Z) color space.
16542     CImg<T>& LabtoXYZ() {
16543 #define _cimg_Labfi(x) ((x)>=0.206893f?((x)*(x)*(x)):(((x)-16.0f/116)/7.787f))
16544 
16545       if (_spectrum!=3)
16546         throw CImgInstanceException(_cimg_instance
16547                                     "LabtoXYZ() : Instance is not a Lab image.",
16548                                     cimg_instance);
16549 
16550       const Tfloat
16551         Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
16552         Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
16553         Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
16554       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16555       for (unsigned int N = _width*_height*_depth; N; --N) {
16556         const Tfloat
16557           L = (Tfloat)*p1,
16558           a = (Tfloat)*p2,
16559           b = (Tfloat)*p3,
16560           cY = (L + 16)/116,
16561           Y = (Tfloat)(Yn*_cimg_Labfi(cY)),
16562           pY = (Tfloat)std::pow(Y/Yn,(Tfloat)1/3),
16563           cX = a/500 + pY,
16564           X = Xn*cX*cX*cX,
16565           cZ = pY - b/200,
16566           Z = Zn*cZ*cZ*cZ;
16567         *(p1++) = (T)(X);
16568         *(p2++) = (T)(Y);
16569         *(p3++) = (T)(Z);
16570       }
16571       return *this;
16572     }
16573 
16574     CImg<Tfloat> get_LabtoXYZ() const {
16575       return CImg<Tfloat>(*this,false).LabtoXYZ();
16576     }
16577 
16578     //! Convert (X,Y,Z)_709 pixels of a color image into the (x,y,Y) color space.
16579     CImg<T>& XYZtoxyY() {
16580       if (_spectrum!=3)
16581         throw CImgInstanceException(_cimg_instance
16582                                     "XYZtoxyY() : Instance is not a XYZ image.",
16583                                     cimg_instance);
16584 
16585       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16586       for (unsigned int N = _width*_height*_depth; N; --N) {
16587         const Tfloat
16588           X = (Tfloat)*p1,
16589           Y = (Tfloat)*p2,
16590           Z = (Tfloat)*p3,
16591           sum = (X+Y+Z),
16592           nsum = sum>0?sum:1;
16593         *(p1++) = (T)(X/nsum);
16594         *(p2++) = (T)(Y/nsum);
16595         *(p3++) = (T)Y;
16596       }
16597       return *this;
16598     }
16599 
16600     CImg<Tfloat> get_XYZtoxyY() const {
16601       return CImg<Tfloat>(*this,false).XYZtoxyY();
16602     }
16603 
16604     //! Convert (x,y,Y) pixels of a color image into the (X,Y,Z)_709 color space.
16605     CImg<T>& xyYtoXYZ() {
16606       if (_spectrum!=3)
16607         throw CImgInstanceException(_cimg_instance
16608                                     "xyYtoXYZ() : Instance is not a xyY image.",
16609                                     cimg_instance);
16610 
16611       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
16612       for (unsigned int N = _width*_height*_depth; N; --N) {
16613         const Tfloat
16614          px = (Tfloat)*p1,
16615          py = (Tfloat)*p2,
16616          Y = (Tfloat)*p3,
16617          ny = py>0?py:1;
16618         *(p1++) = (T)(px*Y/ny);
16619         *(p2++) = (T)Y;
16620         *(p3++) = (T)((1-px-py)*Y/ny);
16621       }
16622       return *this;
16623     }
16624 
16625     CImg<Tfloat> get_xyYtoXYZ() const {
16626       return CImg<Tfloat>(*this,false).xyYtoXYZ();
16627     }
16628 
16629     //! Convert a (R,G,B) image to a (L,a,b) one.
16630     CImg<T>& RGBtoLab() {
16631       return RGBtoXYZ().XYZtoLab();
16632     }
16633 
16634     CImg<Tfloat> get_RGBtoLab() const {
16635       return CImg<Tfloat>(*this,false).RGBtoLab();
16636     }
16637 
16638     //! Convert a (L,a,b) image to a (R,G,B) one.
16639     CImg<T>& LabtoRGB() {
16640       return LabtoXYZ().XYZtoRGB();
16641     }
16642 
16643     CImg<Tuchar> get_LabtoRGB() const {
16644       return CImg<Tuchar>(*this,false).LabtoRGB();
16645     }
16646 
16647     //! Convert a (R,G,B) image to a (x,y,Y) one.
16648     CImg<T>& RGBtoxyY() {
16649       return RGBtoXYZ().XYZtoxyY();
16650     }
16651 
16652     CImg<Tfloat> get_RGBtoxyY() const {
16653       return CImg<Tfloat>(*this,false).RGBtoxyY();
16654     }
16655 
16656     //! Convert a (x,y,Y) image to a (R,G,B) one.
16657     CImg<T>& xyYtoRGB() {
16658       return xyYtoXYZ().XYZtoRGB();
16659     }
16660 
16661     CImg<Tuchar> get_xyYtoRGB() const {
16662       return CImg<Tuchar>(*this,false).xyYtoRGB();
16663     }
16664 
16665     //! Convert a (R,G,B) image to a (C,M,Y,K) one.
16666     CImg<T>& RGBtoCMYK() {
16667       return RGBtoCMY().CMYtoCMYK();
16668     }
16669 
16670     CImg<Tfloat> get_RGBtoCMYK() const {
16671       return CImg<Tfloat>(*this,false).RGBtoCMYK();
16672     }
16673 
16674     //! Convert a (C,M,Y,K) image to a (R,G,B) one.
16675     CImg<T>& CMYKtoRGB() {
16676       return CMYKtoCMY().CMYtoRGB();
16677     }
16678 
16679     CImg<Tuchar> get_CMYKtoRGB() const {
16680       return CImg<Tuchar>(*this,false).CMYKtoRGB();
16681     }
16682 
16683     //! Convert a (R,G,B) image to a Bayer-coded representation.
16684     /**
16685        \note First (upper-left) pixel if the red component of the pixel color.
16686     **/
16687     CImg<T>& RGBtoBayer() {
16688       return get_RGBtoBayer().move_to(*this);
16689     }
16690 
16691     CImg<T> get_RGBtoBayer() const {
16692       if (_spectrum!=3)
16693         throw CImgInstanceException(_cimg_instance
16694                                     "RGBtoBayer() : Instance is not a RGB image.",
16695                                     cimg_instance);
16696 
16697       CImg<T> res(_width,_height,_depth,1);
16698       const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
16699       T *ptrd = res._data;
16700       cimg_forXYZ(*this,x,y,z) {
16701         if (y%2) {
16702           if (x%2) *(ptrd++) = *ptr_b;
16703           else *(ptrd++) = *ptr_g;
16704         } else {
16705           if (x%2) *(ptrd++) = *ptr_g;
16706           else *(ptrd++) = *ptr_r;
16707         }
16708         ++ptr_r; ++ptr_g; ++ptr_b;
16709       }
16710       return res;
16711     }
16712 
16713     //! Convert a Bayer-coded image to a (R,G,B) color image.
16714     CImg<T>& BayertoRGB(const unsigned int interpolation_type=3) {
16715       return get_BayertoRGB(interpolation_type).move_to(*this);
16716     }
16717 
16718     CImg<Tuchar> get_BayertoRGB(const unsigned int interpolation_type=3) const {
16719       if (_spectrum!=1)
16720         throw CImgInstanceException(_cimg_instance
16721                                     "BayertoRGB() : Instance is not a Bayer image.",
16722                                     cimg_instance);
16723 
16724       CImg<Tuchar> res(_width,_height,_depth,3);
16725       CImg_3x3(I,T);
16726       Tuchar *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2);
16727       switch (interpolation_type) {
16728       case 3 : { // Edge-directed
16729         CImg_3x3(R,T);
16730         CImg_3x3(G,T);
16731         CImg_3x3(B,T);
16732         cimg_forXYZ(*this,x,y,z) {
16733           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
16734           cimg_get3x3(*this,x,y,z,0,I,T);
16735           if (y%2) {
16736             if (x%2) {
16737               const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
16738               *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
16739             } else *ptr_g = (Tuchar)Icc;
16740           } else {
16741             if (x%2) *ptr_g = (Tuchar)Icc;
16742             else {
16743               const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
16744               *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
16745             }
16746           }
16747           ++ptr_g;
16748         }
16749         cimg_forXYZ(*this,x,y,z) {
16750           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
16751           cimg_get3x3(*this,x,y,z,0,I,T);
16752           cimg_get3x3(res,x,y,z,1,G,T);
16753           if (y%2) {
16754             if (x%2) *ptr_b = (Tuchar)Icc;
16755             else { *ptr_r = (Tuchar)((Icn+Icp)/2); *ptr_b = (Tuchar)((Inc+Ipc)/2); }
16756           } else {
16757             if (x%2) { *ptr_r = (Tuchar)((Inc+Ipc)/2); *ptr_b = (Tuchar)((Icn+Icp)/2); }
16758             else *ptr_r = (Tuchar)Icc;
16759           }
16760           ++ptr_r; ++ptr_b;
16761         }
16762         ptr_r = res.data(0,0,0,0);
16763         ptr_g = res.data(0,0,0,1);
16764         ptr_b = res.data(0,0,0,2);
16765         cimg_forXYZ(*this,x,y,z) {
16766           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
16767           cimg_get3x3(res,x,y,z,0,R,T);
16768           cimg_get3x3(res,x,y,z,1,G,T);
16769           cimg_get3x3(res,x,y,z,2,B,T);
16770           if (y%2) {
16771             if (x%2) {
16772               const float alpha = (float)cimg::sqr(Rnc-Rpc), beta = (float)cimg::sqr(Rcn-Rcp), cx = 1/(1+alpha), cy = 1/(1+beta);
16773               *ptr_r = (Tuchar)((cx*(Rnc+Rpc) + cy*(Rcn+Rcp))/(2*(cx+cy)));
16774             }
16775           } else {
16776             if (!(x%2)) {
16777               const float alpha = (float)cimg::sqr(Bnc-Bpc), beta = (float)cimg::sqr(Bcn-Bcp), cx = 1/(1+alpha), cy = 1/(1+beta);
16778               *ptr_b = (Tuchar)((cx*(Bnc+Bpc) + cy*(Bcn+Bcp))/(2*(cx+cy)));
16779             }
16780           }
16781           ++ptr_r; ++ptr_g; ++ptr_b;
16782         }
16783       } break;
16784       case 2 : { // Linear interpolation
16785         cimg_forXYZ(*this,x,y,z) {
16786           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
16787           cimg_get3x3(*this,x,y,z,0,I,T);
16788           if (y%2) {
16789             if (x%2) { *ptr_r = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)Icc; }
16790             else { *ptr_r = (Tuchar)((Icp+Icn)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Inc+Ipc)/2); }
16791           } else {
16792             if (x%2) { *ptr_r = (Tuchar)((Ipc+Inc)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Icn+Icp)/2); }
16793             else { *ptr_r = (Tuchar)Icc; *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); }
16794           }
16795           ++ptr_r; ++ptr_g; ++ptr_b;
16796         }
16797       } break;
16798       case 1 : { // Nearest neighbor interpolation
16799         cimg_forXYZ(*this,x,y,z) {
16800           const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
16801           cimg_get3x3(*this,x,y,z,0,I,T);
16802           if (y%2) {
16803             if (x%2) { *ptr_r = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp); *ptr_g = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp); *ptr_b = (Tuchar)Icc; }
16804             else { *ptr_r = (Tuchar)cimg::min(Icn,Icp); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Inc,Ipc); }
16805           } else {
16806             if (x%2) { *ptr_r = (Tuchar)cimg::min(Inc,Ipc); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Icn,Icp); }
16807             else { *ptr_r = (Tuchar)Icc; *ptr_g = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp); *ptr_b = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp); }
16808           }
16809           ++ptr_r; ++ptr_g; ++ptr_b;
16810         }
16811       } break;
16812       default : { // 0-filling interpolation
16813         const T *ptrs = _data;
16814         res.fill(0);
16815         cimg_forXYZ(*this,x,y,z) {
16816           const T val = *(ptrs++);
16817           if (y%2) { if (x%2) *ptr_b = val; else *ptr_g = val; } else { if (x%2) *ptr_g = val; else *ptr_r = val; }
16818           ++ptr_r; ++ptr_g; ++ptr_b;
16819         }
16820       }
16821       }
16822       return res;
16823     }
16824 
16825     //@}
16826     //------------------------------------------
16827     //
16828     //! \name Geometric / Spatial Manipulation
16829     //@{
16830     //------------------------------------------
16831 
16832     static float _cimg_lanczos(const float x) {
16833       if (x<=-2 || x>=2) return 0;
16834       const float a = (float)cimg::PI*x, b = 0.5f*a;
16835       return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
16836     }
16837 
16838     //! Resize an image.
16839     /**
16840        \param size_x Number of columns (new size along the X-axis).
16841        \param size_y Number of rows (new size along the Y-axis).
16842        \param size_z Number of slices (new size along the Z-axis).
16843        \param size_c Number of vector-channels (new size along the C-axis).
16844        \param interpolation_type Method of interpolation :
16845        - -1 = no interpolation : raw memory resizing.
16846        - 0 = no interpolation : additional space is filled according to \p border_condition.
16847        - 1 = nearest-neighbor interpolation.
16848        - 2 = moving average interpolation.
16849        - 3 = linear interpolation.
16850        - 4 = grid interpolation.
16851        - 5 = bicubic interpolation.
16852        - 6 = lanczos interpolation.
16853        \param border_conditions Border condition type.
16854        \param centering_x Set centering type (only if \p interpolation_type=0).
16855        \param centering_y Set centering type (only if \p interpolation_type=0).
16856        \param centering_z Set centering type (only if \p interpolation_type=0).
16857        \param centering_c Set centering type (only if \p interpolation_type=0).
16858        \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
16859     **/
16860     CImg<T>& resize(const int size_x, const int size_y=-100,
16861                     const int size_z=-100, const int size_c=-100,
16862                     const int interpolation_type=1, const unsigned int border_conditions=0,
16863                     const float centering_x = 0, const float centering_y = 0,
16864                     const float centering_z = 0, const float centering_c = 0) {
16865       if (!size_x || !size_y || !size_z || !size_c) return assign();
16866       const unsigned int
16867         _sx = (unsigned int)(size_x<0?-size_x*_width/100:size_x),
16868         _sy = (unsigned int)(size_y<0?-size_y*_height/100:size_y),
16869         _sz = (unsigned int)(size_z<0?-size_z*_depth/100:size_z),
16870         _sc = (unsigned int)(size_c<0?-size_c*_spectrum/100:size_c),
16871         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
16872       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
16873       if (is_empty()) return assign(sx,sy,sz,sc,0);
16874       if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
16875         _width = sx; _height = sy; _depth = sz; _spectrum = sc;
16876         return *this;
16877       }
16878       return get_resize(sx,sy,sz,sc,interpolation_type,border_conditions,centering_x,centering_y,centering_z,centering_c).move_to(*this);
16879     }
16880 
16881     CImg<T> get_resize(const int size_x, const int size_y = -100,
16882                        const int size_z = -100, const int size_c = -100,
16883                        const int interpolation_type=1, const unsigned int border_conditions=0,
16884                        const float centering_x = 0, const float centering_y = 0,
16885                        const float centering_z = 0, const float centering_c = 0) const {
16886       if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
16887           centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
16888         throw CImgArgumentException(_cimg_instance
16889                                     "resize() : Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
16890                                     cimg_instance,
16891                                     centering_x,centering_y,centering_z,centering_c);
16892 
16893       if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
16894       const unsigned int
16895         _sx = (unsigned int)(size_x<0?-size_x*_width/100:size_x),
16896         _sy = (unsigned int)(size_y<0?-size_y*_height/100:size_y),
16897         _sz = (unsigned int)(size_z<0?-size_z*_depth/100:size_z),
16898         _sc = (unsigned int)(size_c<0?-size_c*_spectrum/100:size_c),
16899         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
16900       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
16901       if (is_empty()) return CImg<T>(sx,sy,sz,sc,0);
16902 
16903       CImg<T> res;
16904       switch (interpolation_type) {
16905 
16906         // Raw resizing.
16907         //
16908       case -1 :
16909         std::memcpy(res.assign(sx,sy,sz,sc,0)._data,_data,sizeof(T)*cimg::min(size(),sx*sy*sz*sc));
16910         break;
16911 
16912         // No interpolation.
16913         //
16914       case 0 : {
16915         const int
16916           xc = (int)(centering_x*((int)sx - width())),
16917           yc = (int)(centering_y*((int)sy - height())),
16918           zc = (int)(centering_z*((int)sz - depth())),
16919           cc = (int)(centering_c*((int)sc - spectrum()));
16920 
16921         switch (border_conditions) {
16922         case 2 : { // Cyclic borders.
16923           res.assign(sx,sy,sz,sc);
16924           const int
16925             x0 = ((int)xc%width()) - width(),
16926             y0 = ((int)yc%height()) - height(),
16927             z0 = ((int)zc%depth()) - depth(),
16928             c0 = ((int)cc%spectrum()) - spectrum();
16929           for (int c = c0; c<(int)sc; c+=spectrum())
16930             for (int z = z0; z<(int)sz; z+=depth())
16931               for (int y = y0; y<(int)sy; y+=height())
16932                 for (int x = x0; x<(int)sx; x+=width())
16933                   res.draw_image(x,y,z,c,*this);
16934         } break;
16935         case 1 : { // Neumann borders.
16936           res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
16937           CImg<T> sprite;
16938           if (xc>0) {  // X-backward
16939             res.get_crop(xc,yc,zc,cc,xc,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
16940             for (int x = xc-1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
16941           }
16942           if (xc+width()<(int)sx) { // X-forward
16943             res.get_crop(xc+width()-1,yc,zc,cc,xc+width()-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
16944             for (int x = xc+width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
16945           }
16946           if (yc>0) {  // Y-backward
16947             res.get_crop(0,yc,zc,cc,sx-1,yc,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
16948             for (int y = yc-1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
16949           }
16950           if (yc+height()<(int)sy) { // Y-forward
16951             res.get_crop(0,yc+height()-1,zc,cc,sx-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
16952             for (int y = yc+height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
16953           }
16954           if (zc>0) {  // Z-backward
16955             res.get_crop(0,0,zc,cc,sx-1,sy-1,zc,cc+spectrum()-1).move_to(sprite);
16956             for (int z = zc-1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
16957           }
16958           if (zc+depth()<(int)sz) { // Z-forward
16959             res.get_crop(0,0,zc+depth()-1,cc,sx-1,sy-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
16960             for (int z = zc+depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
16961           }
16962           if (cc>0) {  // C-backward
16963             res.get_crop(0,0,0,cc,sx-1,sy-1,sz-1,cc).move_to(sprite);
16964             for (int c = cc-1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
16965           }
16966           if (cc+spectrum()<(int)sc) { // C-forward
16967             res.get_crop(0,0,0,cc+spectrum()-1,sx-1,sy-1,sz-1,cc+spectrum()-1).move_to(sprite);
16968             for (int c = cc+spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
16969           }
16970         } break;
16971         default : // Dirichlet borders.
16972           res.assign(sx,sy,sz,sc,0).draw_image(xc,yc,zc,cc,*this);
16973         }
16974         break;
16975       } break;
16976 
16977         // Nearest neighbor interpolation.
16978         //
16979       case 1 : {
16980         res.assign(sx,sy,sz,sc);
16981         CImg<uintT> off_x(sx), off_y(sy+1), off_z(sz+1), off_c(sc+1);
16982         unsigned int *poff_x, *poff_y, *poff_z, *poff_c, curr, old;
16983         const unsigned int wh = _width*_height, whd = _width*_height*_depth, sxy = sx*sy, sxyz = sx*sy*sz;
16984         poff_x = off_x._data; curr = 0;
16985         cimg_forX(res,x) { old = curr; curr = (x+1)*_width/sx; *(poff_x++) = (unsigned int)curr - (unsigned int)old; }
16986         poff_y = off_y._data; curr = 0;
16987         cimg_forY(res,y) { old = curr; curr = (y+1)*_height/sy; *(poff_y++) = _width*((unsigned int)curr - (unsigned int)old); } *poff_y = 0;
16988         poff_z = off_z._data; curr = 0;
16989         cimg_forZ(res,z) { old = curr; curr = (z+1)*_depth/sz; *(poff_z++) = wh*((unsigned int)curr - (unsigned int)old); } *poff_z = 0;
16990         poff_c = off_c._data; curr = 0;
16991         cimg_forC(res,c) { old = curr; curr = (c+1)*_spectrum/sc; *(poff_c++) = whd*((unsigned int)curr - (unsigned int)old); } *poff_c = 0;
16992         T *ptrd = res._data;
16993         const T* ptrv = _data;
16994         poff_c = off_c._data;
16995         for (unsigned int c = 0; c<sc; ) {
16996           const T *ptrz = ptrv;
16997           poff_z = off_z._data;
16998           for (unsigned int z = 0; z<sz; ) {
16999             const T *ptry = ptrz;
17000             poff_y = off_y._data;
17001             for (unsigned int y = 0; y<sy; ) {
17002               const T *ptrx = ptry;
17003               poff_x = off_x._data;
17004               cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
17005               ++y;
17006               unsigned int dy = *(poff_y++);
17007               for (;!dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
17008               ptry+=dy;
17009             }
17010             ++z;
17011             unsigned int dz = *(poff_z++);
17012             for (;!dz && z<dz; std::memcpy(ptrd,ptrd-sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
17013             ptrz+=dz;
17014           }
17015           ++c;
17016           unsigned int dc = *(poff_c++);
17017           for (;!dc && c<dc; std::memcpy(ptrd,ptrd-sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
17018           ptrv+=dc;
17019         }
17020       } break;
17021 
17022         // Moving average.
17023         //
17024       case 2 : {
17025         bool instance_first = true;
17026         if (sx!=_width) {
17027           CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
17028           for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
17029             const unsigned int d = cimg::min(b,c);
17030             a-=d; b-=d; c-=d;
17031             cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
17032             if (!b) { cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width; ++t; b = _width; }
17033             if (!c) { ++s; c = sx; }
17034           }
17035           tmp.move_to(res);
17036           instance_first = false;
17037         }
17038         if (sy!=_height) {
17039           CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
17040           for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
17041             const unsigned int d = cimg::min(b,c);
17042             a-=d; b-=d; c-=d;
17043             if (instance_first) cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
17044             else cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
17045             if (!b) { cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height; ++t; b = _height; }
17046             if (!c) { ++s; c = sy; }
17047           }
17048           tmp.move_to(res);
17049           instance_first = false;
17050         }
17051         if (sz!=_depth) {
17052           CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
17053           for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
17054             const unsigned int d = cimg::min(b,c);
17055             a-=d; b-=d; c-=d;
17056             if (instance_first) cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
17057             else cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
17058             if (!b) { cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth; ++t; b = _depth; }
17059             if (!c) { ++s; c = sz; }
17060           }
17061           tmp.move_to(res);
17062           instance_first = false;
17063         }
17064         if (sc!=_spectrum) {
17065           CImg<Tfloat> tmp(sx,sy,sz,sc,0);
17066           for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
17067             const unsigned int d = cimg::min(b,c);
17068             a-=d; b-=d; c-=d;
17069             if (instance_first) cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
17070             else cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
17071             if (!b) { cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum; ++t; b = _spectrum; }
17072             if (!c) { ++s; c = sc; }
17073           }
17074           tmp.move_to(res);
17075           instance_first = false;
17076         }
17077       } break;
17078 
17079         // Linear interpolation.
17080         //
17081       case 3 : {
17082         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
17083         CImg<floatT> foff(off._width);
17084         unsigned int *poff;
17085         float *pfoff, old, curr;
17086         CImg<T> resx, resy, resz, resc;
17087         T *ptrd;
17088 
17089         if (sx!=_width) {
17090           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
17091           else {
17092             const float fx = (!border_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
17093             resx.assign(sx,_height,_depth,_spectrum);
17094             curr = old = 0; poff = off._data; pfoff = foff._data;
17095             cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
17096             ptrd = resx._data;
17097             const T *ptrs0 = _data;
17098             cimg_forYZC(resx,y,z,c) {
17099               poff = off._data; pfoff = foff._data;
17100               const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-1);
17101               cimg_forX(resx,x) {
17102                 const float alpha = *(pfoff++);
17103                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+1):val1;
17104                 *(ptrd++) = (T)((1-alpha)*val1 + alpha*val2);
17105                 ptrs+=*(poff++);
17106               }
17107               ptrs0+=_width;
17108             }
17109           }
17110         } else resx.assign(*this,true);
17111 
17112         if (sy!=_height) {
17113           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
17114           else {
17115             const float fy = (!border_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
17116             resy.assign(sx,sy,_depth,_spectrum);
17117             curr = old = 0; poff = off._data; pfoff = foff._data;
17118             cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
17119             cimg_forXZC(resy,x,z,c) {
17120               ptrd = resy.data(x,0,z,c);
17121               const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height-1)*sx;
17122               poff = off._data; pfoff = foff._data;
17123               cimg_forY(resy,y) {
17124                 const float alpha = *(pfoff++);
17125                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sx):val1;
17126                 *ptrd = (T)((1-alpha)*val1 + alpha*val2);
17127                 ptrd+=sx;
17128                 ptrs+=*(poff++);
17129               }
17130             }
17131           }
17132           resx.assign();
17133         } else resy.assign(resx,true);
17134 
17135         if (sz!=_depth) {
17136           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
17137           else {
17138             const float fz = (!border_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
17139             const unsigned int sxy = sx*sy;
17140             resz.assign(sx,sy,sz,_spectrum);
17141             curr = old = 0; poff = off._data; pfoff = foff._data;
17142             cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
17143             cimg_forXYC(resz,x,y,c) {
17144               ptrd = resz.data(x,y,0,c);
17145               const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth-1)*sxy;
17146               poff = off._data; pfoff = foff._data;
17147               cimg_forZ(resz,z) {
17148                 const float alpha = *(pfoff++);
17149                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxy):val1;
17150                 *ptrd = (T)((1-alpha)*val1 + alpha*val2);
17151                 ptrd+=sxy;
17152                 ptrs+=*(poff++);
17153               }
17154             }
17155           }
17156           resy.assign();
17157         } else resz.assign(resy,true);
17158 
17159         if (sc!=_spectrum) {
17160           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
17161           else {
17162             const float fc = (!border_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
17163             const unsigned int sxyz = sx*sy*sz;
17164             resc.assign(sx,sy,sz,sc);
17165             curr = old = 0; poff = off._data; pfoff = foff._data;
17166             cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
17167             cimg_forXYZ(resc,x,y,z) {
17168               ptrd = resc.data(x,y,z,0);
17169               const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum-1)*sxyz;
17170               poff = off._data; pfoff = foff._data;
17171               cimg_forC(resc,c) {
17172                 const float alpha = *(pfoff++);
17173                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxyz):val1;
17174                 *ptrd = (T)((1-alpha)*val1 + alpha*val2);
17175                 ptrd+=sxyz;
17176                 ptrs+=*(poff++);
17177               }
17178             }
17179           }
17180           resz.assign();
17181         } else resc.assign(resz,true);
17182         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
17183       } break;
17184 
17185         // Grid interpolation.
17186         //
17187       case 4 : {
17188         CImg<T> resx, resy, resz, resc;
17189         const unsigned int sxy = sx*sy, sxyz = sx*sy*sz;
17190         if (sx!=_width) {
17191           if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
17192           else {
17193             resx.assign(sx,_height,_depth,_spectrum,0);
17194             const T *ptrs = _data;
17195             T *ptrd = resx._data + (int)(centering_x*(sx-1)/_width);
17196             cimg_forYZC(*this,y,z,c) {
17197               cimg_forX(*this,x) ptrd[x*sx/_width] = *(ptrs++);
17198               ptrd+=sx;
17199             }
17200           }
17201         } else resx.assign(*this,true);
17202 
17203         if (sy!=_height) {
17204           if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
17205           else {
17206             resy.assign(sx,sy,_depth,_spectrum,0);
17207             const T *ptrs = resx._data;
17208             T *ptrd = resy._data + (int)(centering_y*(sy-1)/_height)*sx;
17209             cimg_forZC(*this,z,c) {
17210               cimg_forY(*this,y) { std::memcpy(ptrd + (y*sy/_height)*sx,ptrs,sizeof(T)*sx); ptrs+=sx; }
17211               ptrd+=sxy;
17212             }
17213           }
17214           resx.assign();
17215         } else resy.assign(resx,true);
17216 
17217         if (sz!=_depth) {
17218           if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
17219           else {
17220             resz.assign(sx,sy,sz,_spectrum,0);
17221             const T *ptrs = resy._data;
17222             T *ptrd = resz._data + (int)(centering_z*(sz-1)/_depth)*sxy;
17223             cimg_forC(*this,c) {
17224               cimg_forZ(*this,z) { std::memcpy(ptrd + (z*sz/_depth)*sxy,ptrs,sizeof(T)*sxy); ptrs+=sxy; }
17225               ptrd+=sxyz;
17226             }
17227           }
17228           resy.assign();
17229         } else resz.assign(resy,true);
17230 
17231         if (sc!=_spectrum) {
17232           if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
17233           else {
17234             resc.assign(sx,sy,sz,sc,0);
17235             const T *ptrs = resz._data;
17236             T *ptrd = resc._data + (int)(centering_c*(sc-1)/_spectrum)*sxyz;
17237             cimg_forC(*this,c) { std::memcpy(ptrd + (c*sc/_spectrum)*sxyz,ptrs,sizeof(T)*sxyz); ptrs+=sxyz; }
17238           }
17239           resz.assign();
17240         } else resc.assign(resz,true);
17241 
17242         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
17243       } break;
17244 
17245         // Bicubic interpolation.
17246         //
17247       case 5 : {
17248         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
17249         CImg<floatT> foff(off._width);
17250         unsigned int *poff;
17251         float *pfoff, old, curr;
17252         CImg<T> resx, resy, resz, resc;
17253         T *ptrd, m, M = max_min(m);
17254 
17255         if (sx!=_width) {
17256           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
17257           else {
17258             const float fx = (!border_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
17259             resx.assign(sx,_height,_depth,_spectrum);
17260             curr = old = 0; poff = off._data; pfoff = foff._data;
17261             cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
17262             ptrd = resx._data;
17263             const T *ptrs0 = _data;
17264             cimg_forYZC(resx,y,z,c) {
17265               poff = off._data; pfoff = foff._data;
17266               const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-2);
17267               cimg_forX(resx,x) {
17268                 const float t = *(pfoff++);
17269                 const Tfloat
17270                   val1 = (Tfloat)*ptrs,
17271                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-1):val1,
17272                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val1,
17273                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val2,
17274                   val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
17275                 *(ptrd++) = (T)(val<m?m:val>M?M:val);
17276                 ptrs+=*(poff++);
17277               }
17278               ptrs0+=_width;
17279             }
17280           }
17281         } else resx.assign(*this,true);
17282 
17283         if (sy!=_height) {
17284           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
17285           else {
17286             const float fy = (!border_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
17287             resy.assign(sx,sy,_depth,_spectrum);
17288             curr = old = 0; poff = off._data; pfoff = foff._data;
17289             cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
17290             cimg_forXZC(resy,x,z,c) {
17291               ptrd = resy.data(x,0,z,c);
17292               const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_height-2)*sx;
17293               poff = off._data; pfoff = foff._data;
17294               cimg_forY(resy,y) {
17295                 const float t = *(pfoff++);
17296                 const Tfloat
17297                   val1 = (Tfloat)*ptrs,
17298                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sx):val1,
17299                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val1,
17300                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val2,
17301                   val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
17302                 *ptrd = (T)(val<m?m:val>M?M:val);
17303                 ptrd+=sx;
17304                 ptrs+=*(poff++);
17305               }
17306             }
17307           }
17308           resx.assign();
17309         } else resy.assign(resx,true);
17310 
17311         if (sz!=_depth) {
17312           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
17313           else {
17314             const float fz = (!border_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
17315             const unsigned int sxy = sx*sy;
17316             resz.assign(sx,sy,sz,_spectrum);
17317             curr = old = 0; poff = off._data; pfoff = foff._data;
17318             cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
17319             cimg_forXYC(resz,x,y,c) {
17320               ptrd = resz.data(x,y,0,c);
17321               const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_depth-2)*sxy;
17322               poff = off._data; pfoff = foff._data;
17323               cimg_forZ(resz,z) {
17324                 const float t = *(pfoff++);
17325                 const Tfloat
17326                   val1 = (Tfloat)*ptrs,
17327                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxy):val1,
17328                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val1,
17329                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val2,
17330                   val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
17331                 *ptrd = (T)(val<m?m:val>M?M:val);
17332                 ptrd+=sxy;
17333                 ptrs+=*(poff++);
17334               }
17335             }
17336           }
17337           resy.assign();
17338         } else resz.assign(resy,true);
17339 
17340         if (sc!=_spectrum) {
17341           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
17342           else {
17343             const float fc = (!border_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
17344             const unsigned int sxyz = sx*sy*sz;
17345             resc.assign(sx,sy,sz,sc);
17346             curr = old = 0; poff = off._data; pfoff = foff._data;
17347             cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
17348             cimg_forXYZ(resc,x,y,z) {
17349               ptrd = resc.data(x,y,z,0);
17350               const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
17351               poff = off._data; pfoff = foff._data;
17352               cimg_forC(resc,c) {
17353                 const float t = *(pfoff++);
17354                 const Tfloat
17355                   val1 = (Tfloat)*ptrs,
17356                   val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxyz):val1,
17357                   val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val1,
17358                   val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val2,
17359                   val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
17360                 *ptrd = (T)(val<m?m:val>M?M:val);
17361                 ptrd+=sxyz;
17362                 ptrs+=*(poff++);
17363               }
17364             }
17365           }
17366           resz.assign();
17367         } else resc.assign(resz,true);
17368 
17369         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
17370       } break;
17371 
17372         // Lanczos interpolation.
17373         //
17374       case 6 : {
17375         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
17376         CImg<floatT> foff(off._width);
17377         unsigned int *poff;
17378         float *pfoff, old, curr;
17379         CImg<T> resx, resy, resz, resc;
17380         T *ptrd, m, M = max_min(m);
17381 
17382         if (sx!=_width) {
17383           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
17384           else {
17385             const float fx = (!border_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
17386             resx.assign(sx,_height,_depth,_spectrum);
17387             curr = old = 0; poff = off._data; pfoff = foff._data;
17388             cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
17389             ptrd = resx._data;
17390             const T *ptrs0 = _data;
17391             cimg_forYZC(resx,y,z,c) {
17392               poff = off._data; pfoff = foff._data;
17393               const T *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, *const ptrsmax = ptrs0 + (_width-2);
17394               cimg_forX(resx,x) {
17395                 const float
17396                   t = *(pfoff++),
17397                   w0 = _cimg_lanczos(t+2),
17398                   w1 = _cimg_lanczos(t+1),
17399                   w2 = _cimg_lanczos(t),
17400                   w3 = _cimg_lanczos(t-1),
17401                   w4 = _cimg_lanczos(t-2);
17402                 const Tfloat
17403                   val2 = (Tfloat)*ptrs,
17404                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-1):val2,
17405                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2):val1,
17406                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val2,
17407                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val3,
17408                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
17409                 *(ptrd++) = (T)(val<m?m:val>M?M:val);
17410                 ptrs+=*(poff++);
17411               }
17412               ptrs0+=_width;
17413             }
17414           }
17415         } else resx.assign(*this,true);
17416 
17417         if (sy!=_height) {
17418           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
17419           else {
17420             const float fy = (!border_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
17421             resy.assign(sx,sy,_depth,_spectrum);
17422             curr = old = 0; poff = off._data; pfoff = foff._data;
17423             cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
17424             cimg_forXZC(resy,x,z,c) {
17425               ptrd = resy.data(x,0,z,c);
17426               const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, *const ptrsmax = ptrs0 + (_height-2)*sx;
17427               poff = off._data; pfoff = foff._data;
17428               cimg_forY(resy,y) {
17429                 const float
17430                   t = *(pfoff++),
17431                   w0 = _cimg_lanczos(t+2),
17432                   w1 = _cimg_lanczos(t+1),
17433                   w2 = _cimg_lanczos(t),
17434                   w3 = _cimg_lanczos(t-1),
17435                   w4 = _cimg_lanczos(t-2);
17436                 const Tfloat
17437                   val2 = (Tfloat)*ptrs,
17438                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sx):val2,
17439                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sx):val1,
17440                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val2,
17441                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val3,
17442                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
17443                 *ptrd = (T)(val<m?m:val>M?M:val);
17444                 ptrd+=sx;
17445                 ptrs+=*(poff++);
17446               }
17447             }
17448           }
17449           resx.assign();
17450         } else resy.assign(resx,true);
17451 
17452         if (sz!=_depth) {
17453           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
17454           else {
17455             const float fz = (!border_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
17456             const unsigned int sxy = sx*sy;
17457             resz.assign(sx,sy,sz,_spectrum);
17458             curr = old = 0; poff = off._data; pfoff = foff._data;
17459             cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
17460             cimg_forXYC(resz,x,y,c) {
17461               ptrd = resz.data(x,y,0,c);
17462               const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, *const ptrsmax = ptrs0 + (_depth-2)*sxy;
17463               poff = off._data; pfoff = foff._data;
17464               cimg_forZ(resz,z) {
17465                 const float
17466                   t = *(pfoff++),
17467                   w0 = _cimg_lanczos(t+2),
17468                   w1 = _cimg_lanczos(t+1),
17469                   w2 = _cimg_lanczos(t),
17470                   w3 = _cimg_lanczos(t-1),
17471                   w4 = _cimg_lanczos(t-2);
17472                 const Tfloat
17473                   val2 = (Tfloat)*ptrs,
17474                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxy):val2,
17475                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxy):val1,
17476                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val2,
17477                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val3,
17478                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
17479                 *ptrd = (T)(val<m?m:val>M?M:val);
17480                 ptrd+=sxy;
17481                 ptrs+=*(poff++);
17482               }
17483             }
17484           }
17485           resy.assign();
17486         } else resz.assign(resy,true);
17487 
17488         if (sc!=_spectrum) {
17489           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
17490           else {
17491             const float fc = (!border_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
17492             const unsigned int sxyz = sx*sy*sz;
17493             resc.assign(sx,sy,sz,sc);
17494             curr = old = 0; poff = off._data; pfoff = foff._data;
17495             cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
17496             cimg_forXYZ(resc,x,y,z) {
17497               ptrd = resc.data(x,y,z,0);
17498               const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
17499               poff = off._data; pfoff = foff._data;
17500               cimg_forC(resc,c) {
17501                 const float
17502                   t = *(pfoff++),
17503                   w0 = _cimg_lanczos(t+2),
17504                   w1 = _cimg_lanczos(t+1),
17505                   w2 = _cimg_lanczos(t),
17506                   w3 = _cimg_lanczos(t-1),
17507                   w4 = _cimg_lanczos(t-2);
17508                 const Tfloat
17509                   val2 = (Tfloat)*ptrs,
17510                   val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxyz):val2,
17511                   val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxyz):val1,
17512                   val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val2,
17513                   val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val3,
17514                   val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
17515                 *ptrd = (T)(val<m?m:val>M?M:val);
17516                 ptrd+=sxyz;
17517                 ptrs+=*(poff++);
17518               }
17519             }
17520           }
17521           resz.assign();
17522         } else resc.assign(resz,true);
17523 
17524         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
17525       } break;
17526 
17527         // Unknow interpolation.
17528         //
17529       default :
17530         throw CImgArgumentException(_cimg_instance
17531                                     "resize() : Invalid specified interpolation %d "
17532                                     "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | 5=bicubic | 6=lanczos }).",
17533                                     cimg_instance,
17534                                     interpolation_type);
17535       }
17536       return res;
17537     }
17538 
17539     //! Resize an image.
17540     template<typename t>
17541     CImg<T>& resize(const CImg<t>& src,
17542                     const int interpolation_type=1, const unsigned int border_conditions=0,
17543                     const float centering_x = 0, const float centering_y = 0,
17544                     const float centering_z = 0, const float centering_c = 0) {
17545       return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,border_conditions,
17546                     centering_x,centering_y,centering_z,centering_c);
17547     }
17548 
17549     template<typename t>
17550     CImg<T> get_resize(const CImg<t>& src,
17551                        const int interpolation_type=1, const unsigned int border_conditions=0,
17552                        const float centering_x = 0, const float centering_y = 0,
17553                        const float centering_z = 0, const float centering_c = 0) const {
17554       return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,border_conditions,
17555                         centering_x,centering_y,centering_z,centering_c);
17556     }
17557 
17558     //! Resize an image.
17559     CImg<T>& resize(const CImgDisplay& disp,
17560                     const int interpolation_type=1, const unsigned int border_conditions=0,
17561                     const float centering_x = 0, const float centering_y = 0,
17562                     const float centering_z = 0, const float centering_c = 0) {
17563       return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,border_conditions,
17564                     centering_x,centering_y,centering_z,centering_c);
17565     }
17566 
17567     CImg<T> get_resize(const CImgDisplay& disp,
17568                        const int interpolation_type=1, const unsigned int border_conditions=0,
17569                        const float centering_x = 0, const float centering_y = 0,
17570                        const float centering_z = 0, const float centering_c = 0) const {
17571       return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,border_conditions,
17572                         centering_x,centering_y,centering_z,centering_c);
17573     }
17574 
17575     //! Half-resize an image, using a special optimized filter.
17576     CImg<T>& resize_halfXY() {
17577       return get_resize_halfXY().move_to(*this);
17578     }
17579 
17580     CImg<T> get_resize_halfXY() const {
17581       if (is_empty()) return *this;
17582       const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
17583                               0.1231940459f,  0.1935127547f, 0.1231940459f,
17584                               0.07842776544f, 0.1231940459f, 0.07842776544f };
17585       T I[9] = { 0 };
17586       CImg<T> res(_width/2,_height/2,_depth,_spectrum);
17587       T *ptrd = res._data;
17588       cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
17589         if (x%2 && y%2) *(ptrd++) = (T)
17590                           (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] +
17591                            I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] +
17592                            I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]);
17593       return res;
17594     }
17595 
17596     //! Upscale an image by a factor 2x.
17597     /**
17598        Use anisotropic upscaling algorithm described at
17599        http://scale2x.sourceforge.net/algorithm.html
17600     **/
17601     CImg<T>& resize_doubleXY() {
17602       return get_resize_doubleXY().move_to(*this);
17603     }
17604 
17605     CImg<T> get_resize_doubleXY() const {
17606 #define _cimg_gs2x_for3(bound,i) \
17607  for (int i = 0, _p1##i = 0, \
17608       _n1##i = 1>=(bound)?(int)(bound)-1:1; \
17609       _n1##i<(int)(bound) || i==--_n1##i; \
17610       _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
17611 
17612 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
17613   _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
17614    _p1##x = 0, \
17615    _n1##x = (int)( \
17616    (I[1] = (T)(img)(0,_p1##y,z,c)), \
17617    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
17618    (I[7] = (T)(img)(0,_n1##y,z,c)),     \
17619    1>=(img)._width?(img).width()-1:1); \
17620    (_n1##x<(img).width() && ( \
17621    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
17622    (I[5] = (T)(img)(_n1##x,y,z,c)), \
17623    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
17624    x==--_n1##x; \
17625    I[1] = I[2], \
17626    I[3] = I[4], I[4] = I[5], \
17627    I[7] = I[8], \
17628    _p1##x = x++, ++_n1##x)
17629 
17630       if (is_empty()) return *this;
17631       CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
17632       CImg_3x3(I,T);
17633       cimg_forZC(*this,z,c) {
17634         T
17635           *ptrd1 = res.data(0,0,0,c),
17636           *ptrd2 = ptrd1 + res._width;
17637         _cimg_gs2x_for3x3(*this,x,y,0,c,I,T) {
17638           if (Icp!=Icn && Ipc!=Inc) {
17639             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
17640             *(ptrd1++) = Icp==Inc?Inc:Icc;
17641             *(ptrd2++) = Ipc==Icn?Ipc:Icc;
17642             *(ptrd2++) = Icn==Inc?Inc:Icc;
17643           } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
17644         }
17645       }
17646       return res;
17647     }
17648 
17649     //! Upscale an image by a factor 3x.
17650     /**
17651        Use anisotropic upscaling algorithm described at
17652        http://scale2x.sourceforge.net/algorithm.html
17653     **/
17654     CImg<T>& resize_tripleXY() {
17655       return get_resize_tripleXY().move_to(*this);
17656     }
17657 
17658     CImg<T> get_resize_tripleXY() const {
17659 #define _cimg_gs3x_for3(bound,i) \
17660  for (int i = 0, _p1##i = 0, \
17661       _n1##i = 1>=(bound)?(int)(bound)-1:1; \
17662       _n1##i<(int)(bound) || i==--_n1##i; \
17663       _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
17664 
17665 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
17666   _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
17667    _p1##x = 0, \
17668    _n1##x = (int)( \
17669    (I[0] = I[1] = (T)(img)(0,_p1##y,z,c)), \
17670    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
17671    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),      \
17672    1>=(img)._width?(img).width()-1:1); \
17673    (_n1##x<(img).width() && ( \
17674    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
17675    (I[5] = (T)(img)(_n1##x,y,z,c)), \
17676    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
17677    x==--_n1##x; \
17678    I[0] = I[1], I[1] = I[2], \
17679    I[3] = I[4], I[4] = I[5], \
17680    I[6] = I[7], I[7] = I[8], \
17681    _p1##x = x++, ++_n1##x)
17682 
17683       if (is_empty()) return *this;
17684       CImg<T> res(3*_width,3*_height,_depth,_spectrum);
17685       CImg_3x3(I,T);
17686       cimg_forZC(*this,z,c) {
17687         T
17688           *ptrd1 = res.data(0,0,0,c),
17689           *ptrd2 = ptrd1 + res._width,
17690           *ptrd3 = ptrd2 + res._width;
17691         _cimg_gs3x_for3x3(*this,x,y,0,c,I,T) {
17692           if (Icp != Icn && Ipc != Inc) {
17693             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
17694             *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
17695             *(ptrd1++) = Icp==Inc?Inc:Icc;
17696             *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
17697             *(ptrd2++) = Icc;
17698             *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
17699             *(ptrd3++) = Ipc==Icn?Ipc:Icc;
17700             *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
17701             *(ptrd3++) = Icn==Inc?Inc:Icc;
17702           } else {
17703             *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
17704             *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
17705             *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
17706           }
17707         }
17708       }
17709       return res;
17710     }
17711 
17712     //! Mirror an image along the specified axis.
17713     CImg<T>& mirror(const char axis) {
17714       if (is_empty()) return *this;
17715       T *pf, *pb, *buf = 0;
17716       switch (cimg::uncase(axis)) {
17717       case 'x' : {
17718         pf = _data; pb = data(_width-1);
17719         const unsigned int width2 = _width/2;
17720         for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
17721           for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
17722           pf+=_width - width2;
17723           pb+=_width + width2;
17724         }
17725       } break;
17726       case 'y' : {
17727         buf = new T[_width];
17728         pf = _data; pb = data(0,_height-1);
17729         const unsigned int height2 = _height/2;
17730         for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
17731           for (unsigned int y = 0; y<height2; ++y) {
17732             std::memcpy(buf,pf,_width*sizeof(T));
17733             std::memcpy(pf,pb,_width*sizeof(T));
17734             std::memcpy(pb,buf,_width*sizeof(T));
17735             pf+=_width;
17736             pb-=_width;
17737           }
17738           pf+=_width*(_height - height2);
17739           pb+=_width*(_height + height2);
17740         }
17741       } break;
17742       case 'z' : {
17743         buf = new T[_width*_height];
17744         pf = _data; pb = data(0,0,_depth-1);
17745         const unsigned int depth2 = _depth/2;
17746         cimg_forC(*this,c) {
17747           for (unsigned int z = 0; z<depth2; ++z) {
17748             std::memcpy(buf,pf,_width*_height*sizeof(T));
17749             std::memcpy(pf,pb,_width*_height*sizeof(T));
17750             std::memcpy(pb,buf,_width*_height*sizeof(T));
17751             pf+=_width*_height;
17752             pb-=_width*_height;
17753           }
17754           pf+=_width*_height*(_depth - depth2);
17755           pb+=_width*_height*(_depth + depth2);
17756         }
17757       } break;
17758       default : {
17759         buf = new T[_width*_height*_depth];
17760         pf = _data; pb = data(0,0,0,_spectrum-1);
17761         const unsigned int _spectrum2 = _spectrum/2;
17762         for (unsigned int v = 0; v<_spectrum2; ++v) {
17763           std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
17764           std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
17765           std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
17766           pf+=_width*_height*_depth;
17767           pb-=_width*_height*_depth;
17768         }
17769       }
17770       }
17771       if (buf) delete[] buf;
17772       return *this;
17773     }
17774 
17775     CImg<T> get_mirror(const char axis) const {
17776       return (+*this).mirror(axis);
17777     }
17778 
17779     //! Shift the image.
17780     /**
17781        \param deltax Amount of displacement along the X-axis.
17782        \param deltay Amount of displacement along the Y-axis.
17783        \param deltaz Amount of displacement along the Z-axis.
17784        \param deltac Amount of displacement along the C-axis.
17785        \param border_condition Border condition.
17786 
17787        - \c border_condition can be :
17788           - 0 : Zero border condition (Dirichlet).
17789           - 1 : Nearest neighbors (Neumann).
17790           - 2 : Repeat Pattern (Fourier style).
17791     **/
17792     CImg<T>& shift(const int deltax, const int deltay=0, const int deltaz=0, const int deltac=0,
17793                        const int border_condition=0) {
17794       if (is_empty()) return *this;
17795       if (deltax) // Shift along X-axis
17796         switch (border_condition) {
17797         case 0 :
17798           if (cimg::abs(deltax)>=width()) return fill(0);
17799           if (deltax<0) cimg_forYZC(*this,y,z,c) {
17800             std::memmove(data(0,y,z,c),data(-deltax,y,z,c),(_width+deltax)*sizeof(T));
17801             std::memset(data(_width+deltax,y,z,c),0,-deltax*sizeof(T));
17802           } else cimg_forYZC(*this,y,z,c) {
17803             std::memmove(data(deltax,y,z,c),data(0,y,z,c),(_width-deltax)*sizeof(T));
17804             std::memset(data(0,y,z,c),0,deltax*sizeof(T));
17805           }
17806           break;
17807         case 1 :
17808           if (deltax<0) {
17809             const int ndeltax = (-deltax>=width())?_width-1:-deltax;
17810             if (!ndeltax) return *this;
17811             cimg_forYZC(*this,y,z,c) {
17812               std::memmove(data(0,y,z,c),data(ndeltax,y,z,c),(_width-ndeltax)*sizeof(T));
17813               T *ptrd = data(_width-1,y,z,c);
17814               const T val = *ptrd;
17815               for (int l = 0; l<ndeltax-1; ++l) *(--ptrd) = val;
17816             }
17817           } else {
17818             const int ndeltax = (deltax>=width())?_width-1:deltax;
17819             if (!ndeltax) return *this;
17820             cimg_forYZC(*this,y,z,c) {
17821               std::memmove(data(ndeltax,y,z,c),data(0,y,z,c),(_width-ndeltax)*sizeof(T));
17822               T *ptrd = data(0,y,z,c);
17823               const T val = *ptrd;
17824               for (int l = 0; l<ndeltax-1; ++l) *(++ptrd) = val;
17825             }
17826           }
17827           break;
17828         default : {
17829           const int ml = cimg::mod(-deltax,width()), ndeltax = (ml<=width()/2)?ml:(ml-width());
17830           if (!ndeltax) return *this;
17831           T *const buf = new T[cimg::abs(ndeltax)];
17832           if (ndeltax>0) cimg_forYZC(*this,y,z,c) {
17833             std::memcpy(buf,data(0,y,z,c),ndeltax*sizeof(T));
17834             std::memmove(data(0,y,z,c),data(ndeltax,y,z,c),(_width-ndeltax)*sizeof(T));
17835             std::memcpy(data(_width-ndeltax,y,z,c),buf,ndeltax*sizeof(T));
17836           } else cimg_forYZC(*this,y,z,c) {
17837             std::memcpy(buf,data(_width+ndeltax,y,z,c),-ndeltax*sizeof(T));
17838             std::memmove(data(-ndeltax,y,z,c),data(0,y,z,c),(_width+ndeltax)*sizeof(T));
17839             std::memcpy(data(0,y,z,c),buf,-ndeltax*sizeof(T));
17840           }
17841           delete[] buf;
17842         }
17843         }
17844 
17845       if (deltay) // Shift along Y-axis
17846         switch (border_condition) {
17847         case 0 :
17848           if (cimg::abs(deltay)>=height()) return fill(0);
17849           if (deltay<0) cimg_forZC(*this,z,c) {
17850             std::memmove(data(0,0,z,c),data(0,-deltay,z,c),_width*(_height+deltay)*sizeof(T));
17851             std::memset(data(0,_height+deltay,z,c),0,-deltay*_width*sizeof(T));
17852           } else cimg_forZC(*this,z,c) {
17853             std::memmove(data(0,deltay,z,c),data(0,0,z,c),_width*(_height-deltay)*sizeof(T));
17854             std::memset(data(0,0,z,c),0,deltay*_width*sizeof(T));
17855           }
17856           break;
17857         case 1 :
17858           if (deltay<0) {
17859             const int ndeltay = (-deltay>=height())?_height-1:-deltay;
17860             if (!ndeltay) return *this;
17861             cimg_forZC(*this,z,c) {
17862               std::memmove(data(0,0,z,c),data(0,ndeltay,z,c),_width*(_height-ndeltay)*sizeof(T));
17863               T *ptrd = data(0,_height-ndeltay,z,c), *ptrs = data(0,_height-1,z,c);
17864               for (int l = 0; l<ndeltay-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
17865             }
17866           } else {
17867             const int ndeltay = (deltay>=height())?_height-1:deltay;
17868             if (!ndeltay) return *this;
17869             cimg_forZC(*this,z,c) {
17870               std::memmove(data(0,ndeltay,z,c),data(0,0,z,c),_width*(_height-ndeltay)*sizeof(T));
17871               T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
17872               for (int l = 0; l<ndeltay-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
17873             }
17874           }
17875           break;
17876         default : {
17877           const int ml = cimg::mod(-deltay,height()), ndeltay = (ml<=height()/2)?ml:(ml-height());
17878           if (!ndeltay) return *this;
17879           T *const buf = new T[_width*cimg::abs(ndeltay)];
17880           if (ndeltay>0) cimg_forZC(*this,z,c) {
17881             std::memcpy(buf,data(0,0,z,c),_width*ndeltay*sizeof(T));
17882             std::memmove(data(0,0,z,c),data(0,ndeltay,z,c),_width*(_height-ndeltay)*sizeof(T));
17883             std::memcpy(data(0,_height-ndeltay,z,c),buf,_width*ndeltay*sizeof(T));
17884           } else cimg_forZC(*this,z,c) {
17885             std::memcpy(buf,data(0,_height+ndeltay,z,c),-ndeltay*_width*sizeof(T));
17886             std::memmove(data(0,-ndeltay,z,c),data(0,0,z,c),_width*(_height+ndeltay)*sizeof(T));
17887             std::memcpy(data(0,0,z,c),buf,-ndeltay*_width*sizeof(T));
17888           }
17889           delete[] buf;
17890         }
17891         }
17892 
17893       if (deltaz) // Shift along Z-axis
17894         switch (border_condition) {
17895         case 0 :
17896           if (cimg::abs(deltaz)>=depth()) return fill(0);
17897           if (deltaz<0) cimg_forC(*this,c) {
17898             std::memmove(data(0,0,0,c),data(0,0,-deltaz,c),_width*_height*(_depth+deltaz)*sizeof(T));
17899             std::memset(data(0,0,_depth+deltaz,c),0,_width*_height*(-deltaz)*sizeof(T));
17900           } else cimg_forC(*this,c) {
17901             std::memmove(data(0,0,deltaz,c),data(0,0,0,c),_width*_height*(_depth-deltaz)*sizeof(T));
17902             std::memset(data(0,0,0,c),0,deltaz*_width*_height*sizeof(T));
17903           }
17904           break;
17905         case 1 :
17906           if (deltaz<0) {
17907             const int ndeltaz = (-deltaz>=depth())?_depth-1:-deltaz;
17908             if (!ndeltaz) return *this;
17909             cimg_forC(*this,c) {
17910               std::memmove(data(0,0,0,c),data(0,0,ndeltaz,c),_width*_height*(_depth-ndeltaz)*sizeof(T));
17911               T *ptrd = data(0,0,_depth-ndeltaz,c), *ptrs = data(0,0,_depth-1,c);
17912               for (int l = 0; l<ndeltaz-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=_width*_height; }
17913             }
17914           } else {
17915             const int ndeltaz = (deltaz>=depth())?_depth-1:deltaz;
17916             if (!ndeltaz) return *this;
17917             cimg_forC(*this,c) {
17918               std::memmove(data(0,0,ndeltaz,c),data(0,0,0,c),_width*_height*(_depth-ndeltaz)*sizeof(T));
17919               T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
17920               for (int l = 0; l<ndeltaz-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=_width*_height; }
17921             }
17922           }
17923           break;
17924         default : {
17925           const int ml = cimg::mod(-deltaz,depth()), ndeltaz = (ml<=depth()/2)?ml:(ml-depth());
17926           if (!ndeltaz) return *this;
17927           T *const buf = new T[_width*_height*cimg::abs(ndeltaz)];
17928           if (ndeltaz>0) cimg_forC(*this,c) {
17929             std::memcpy(buf,data(0,0,0,c),_width*_height*ndeltaz*sizeof(T));
17930             std::memmove(data(0,0,0,c),data(0,0,ndeltaz,c),_width*_height*(_depth-ndeltaz)*sizeof(T));
17931             std::memcpy(data(0,0,_depth-ndeltaz,c),buf,_width*_height*ndeltaz*sizeof(T));
17932           } else cimg_forC(*this,c) {
17933             std::memcpy(buf,data(0,0,_depth+ndeltaz,c),-ndeltaz*_width*_height*sizeof(T));
17934             std::memmove(data(0,0,-ndeltaz,c),data(0,0,0,c),_width*_height*(_depth+ndeltaz)*sizeof(T));
17935             std::memcpy(data(0,0,0,c),buf,-ndeltaz*_width*_height*sizeof(T));
17936           }
17937           delete[] buf;
17938         }
17939         }
17940 
17941       if (deltac) // Shift along C-axis
17942         switch (border_condition) {
17943         case 0 :
17944           if (cimg::abs(deltac)>=spectrum()) return fill(0);
17945           if (-deltac>0) {
17946             std::memmove(_data,data(0,0,0,-deltac),_width*_height*_depth*(_spectrum+deltac)*sizeof(T));
17947             std::memset(data(0,0,0,_spectrum+deltac),0,_width*_height*_depth*(-deltac)*sizeof(T));
17948           } else cimg_forC(*this,c) {
17949             std::memmove(data(0,0,0,deltac),_data,_width*_height*_depth*(_spectrum-deltac)*sizeof(T));
17950             std::memset(_data,0,deltac*_width*_height*_depth*sizeof(T));
17951           }
17952           break;
17953         case 1 :
17954           if (deltac<0) {
17955             const int ndeltac = (-deltac>=spectrum())?_spectrum-1:-deltac;
17956             if (!ndeltac) return *this;
17957             std::memmove(_data,data(0,0,0,ndeltac),_width*_height*_depth*(_spectrum-ndeltac)*sizeof(T));
17958             T *ptrd = data(0,0,0,_spectrum-ndeltac), *ptrs = data(0,0,0,_spectrum-1);
17959             for (int l = 0; l<ndeltac-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=_width*_height*_depth; }
17960           } else {
17961             const int ndeltac = (deltac>=spectrum())?_spectrum-1:deltac;
17962             if (!ndeltac) return *this;
17963             std::memmove(data(0,0,0,ndeltac),_data,_width*_height*_depth*(_spectrum-ndeltac)*sizeof(T));
17964             T *ptrd = data(0,0,0,1);
17965             for (int l = 0; l<ndeltac-1; ++l) { std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=_width*_height*_depth; }
17966           }
17967           break;
17968         default : {
17969           const int ml = cimg::mod(-deltac,spectrum()), ndeltac = (ml<=spectrum()/2)?ml:(ml-spectrum());
17970           if (!ndeltac) return *this;
17971           T *const buf = new T[_width*_height*_depth*cimg::abs(ndeltac)];
17972           if (ndeltac>0) {
17973             std::memcpy(buf,_data,_width*_height*_depth*ndeltac*sizeof(T));
17974             std::memmove(_data,data(0,0,0,ndeltac),_width*_height*_depth*(_spectrum-ndeltac)*sizeof(T));
17975             std::memcpy(data(0,0,0,_spectrum-ndeltac),buf,_width*_height*_depth*ndeltac*sizeof(T));
17976           } else {
17977             std::memcpy(buf,data(0,0,0,_spectrum+ndeltac),-ndeltac*_width*_height*_depth*sizeof(T));
17978             std::memmove(data(0,0,0,-ndeltac),_data,_width*_height*_depth*(_spectrum+ndeltac)*sizeof(T));
17979             std::memcpy(_data,buf,-ndeltac*_width*_height*_depth*sizeof(T));
17980           }
17981           delete[] buf;
17982         }
17983         }
17984       return *this;
17985     }
17986 
17987     CImg<T> get_shift(const int deltax, const int deltay=0, const int deltaz=0, const int deltac=0,
17988                           const int border_condition=0) const {
17989       return (+*this).shift(deltax,deltay,deltaz,deltac,border_condition);
17990     }
17991 
17992     // Permute axes order (internal).
17993     template<typename t>
17994     CImg<t> _get_permute_axes(const char *const permut, const t&) const {
17995       if (is_empty() || !permut) return CImg<t>(*this,false);
17996       CImg<t> res;
17997       const T* ptrs = _data;
17998       if (!cimg::strncasecmp(permut,"xyzc",4)) return (+*this);
17999       if (!cimg::strncasecmp(permut,"xycz",4)) {
18000         res.assign(_width,_height,_spectrum,_depth);
18001         cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z) = (t)*(ptrs++);
18002       }
18003       if (!cimg::strncasecmp(permut,"xzyc",4)) {
18004         res.assign(_width,_depth,_height,_spectrum);
18005         cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c) = (t)*(ptrs++);
18006       }
18007       if (!cimg::strncasecmp(permut,"xzcy",4)) {
18008         res.assign(_width,_depth,_spectrum,_height);
18009         cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y) = (t)*(ptrs++);
18010       }
18011       if (!cimg::strncasecmp(permut,"xcyz",4)) {
18012         res.assign(_width,_spectrum,_height,_depth);
18013         cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z) = (t)*(ptrs++);
18014       }
18015       if (!cimg::strncasecmp(permut,"xczy",4)) {
18016         res.assign(_width,_spectrum,_depth,_height);
18017         cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y) = (t)*(ptrs++);
18018       }
18019       if (!cimg::strncasecmp(permut,"yxzc",4)) {
18020         res.assign(_height,_width,_depth,_spectrum);
18021         cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c) = (t)*(ptrs++);
18022       }
18023       if (!cimg::strncasecmp(permut,"yxcz",4)) {
18024         res.assign(_height,_width,_spectrum,_depth);
18025         cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z) = (t)*(ptrs++);
18026       }
18027       if (!cimg::strncasecmp(permut,"yzxc",4)) {
18028         res.assign(_height,_depth,_width,_spectrum);
18029         cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c) = (t)*(ptrs++);
18030       }
18031       if (!cimg::strncasecmp(permut,"yzcx",4)) {
18032         res.assign(_height,_depth,_spectrum,_width);
18033         switch (_width) {
18034         case 1 : {
18035           t *ptr_r = res.data(0,0,0,0);
18036           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
18037             *(ptr_r++) = (t)*(ptrs++);
18038           }
18039         } break;
18040         case 2 : {
18041           t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
18042           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
18043             *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++);
18044           }
18045         } break;
18046         case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
18047           t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2);
18048           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
18049             *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++);
18050           }
18051         } break;
18052         case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
18053           t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
18054           for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
18055             *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++); *(ptr_a++) = (t)*(ptrs++);
18056           }
18057         } break;
18058         default : {
18059           cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x) = *(ptrs++);
18060           return res;
18061         }
18062         }
18063       }
18064       if (!cimg::strncasecmp(permut,"ycxz",4)) {
18065         res.assign(_height,_spectrum,_width,_depth);
18066         cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z) = (t)*(ptrs++);
18067       }
18068       if (!cimg::strncasecmp(permut,"yczx",4)) {
18069         res.assign(_height,_spectrum,_depth,_width);
18070         cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x) = (t)*(ptrs++);
18071       }
18072       if (!cimg::strncasecmp(permut,"zxyc",4)) {
18073         res.assign(_depth,_width,_height,_spectrum);
18074         cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c) = (t)*(ptrs++);
18075       }
18076       if (!cimg::strncasecmp(permut,"zxcy",4)) {
18077         res.assign(_depth,_width,_spectrum,_height);
18078         cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y) = (t)*(ptrs++);
18079       }
18080       if (!cimg::strncasecmp(permut,"zyxc",4)) {
18081         res.assign(_depth,_height,_width,_spectrum);
18082         cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c) = (t)*(ptrs++);
18083       }
18084       if (!cimg::strncasecmp(permut,"zycx",4)) {
18085         res.assign(_depth,_height,_spectrum,_width);
18086         cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x) = (t)*(ptrs++);
18087       }
18088       if (!cimg::strncasecmp(permut,"zcxy",4)) {
18089         res.assign(_depth,_spectrum,_width,_height);
18090         cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y) = (t)*(ptrs++);
18091       }
18092       if (!cimg::strncasecmp(permut,"zcyx",4)) {
18093         res.assign(_depth,_spectrum,_height,_width);
18094         cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x) = (t)*(ptrs++);
18095       }
18096       if (!cimg::strncasecmp(permut,"cxyz",4)) {
18097         res.assign(_spectrum,_width,_height,_depth);
18098         switch (_spectrum) {
18099         case 1 : {
18100           const T *ptr_r = data(0,0,0,0);
18101           t *ptrd = res._data;
18102           for (unsigned int siz = _width*_height*_depth; siz; --siz) {
18103             *(ptrd++) = (t)*(ptr_r++);
18104           }
18105         } break;
18106         case 2 : {
18107           const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
18108           t *ptrd = res._data;
18109           for (unsigned int siz = _width*_height*_depth; siz; --siz) {
18110             *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++);
18111           }
18112         } break;
18113         case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
18114           const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
18115           t *ptrd = res._data;
18116           for (unsigned int siz = _width*_height*_depth; siz; --siz) {
18117             *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++);
18118           }
18119         } break;
18120         case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
18121           const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
18122           t *ptrd = res._data;
18123           for (unsigned int siz = _width*_height*_depth; siz; --siz) {
18124             *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); *(ptrd++) = (t)*(ptr_a++);
18125           }
18126         } break;
18127         default : {
18128           cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z) = (t)*(ptrs++);
18129         }
18130         }
18131       }
18132       if (!cimg::strncasecmp(permut,"cxzy",4)) {
18133         res.assign(_spectrum,_width,_depth,_height);
18134         cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y) = (t)*(ptrs++);
18135       }
18136       if (!cimg::strncasecmp(permut,"cyxz",4)) {
18137         res.assign(_spectrum,_height,_width,_depth);
18138         cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z) = (t)*(ptrs++);
18139       }
18140       if (!cimg::strncasecmp(permut,"cyzx",4)) {
18141         res.assign(_spectrum,_height,_depth,_width);
18142         cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x) = (t)*(ptrs++);
18143       }
18144       if (!cimg::strncasecmp(permut,"czxy",4)) {
18145         res.assign(_spectrum,_depth,_width,_height);
18146         cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y) = (t)*(ptrs++);
18147       }
18148       if (!cimg::strncasecmp(permut,"czyx",4)) {
18149         res.assign(_spectrum,_depth,_height,_width);
18150         cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x) = (t)*(ptrs++);
18151       }
18152       if (!res)
18153         throw CImgArgumentException(_cimg_instance
18154                                     "permute_axes() : Invalid specified permutation '%s'.",
18155                                     cimg_instance,
18156                                     permut);
18157       return res;
18158     }
18159 
18160     //! Permute axes order.
18161     /**
18162        This function permutes image axes.
18163        \param permut = String describing the permutation (4 characters).
18164     **/
18165     CImg<T>& permute_axes(const char *const order) {
18166       return get_permute_axes(order).move_to(*this);
18167     }
18168 
18169     CImg<T> get_permute_axes(const char *const order) const {
18170       const T foo = (T)0;
18171       return _get_permute_axes(order,foo);
18172     }
18173 
18174     //! Unroll all images values into specified axis.
18175     CImg<T>& unroll(const char axis) {
18176       const unsigned int siz = size();
18177       if (siz) switch (axis) {
18178       case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
18179       case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
18180       case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
18181       default : _spectrum = siz; _width = _height = _depth = 1;
18182       }
18183       return *this;
18184     }
18185 
18186     CImg<T> get_unroll(const char axis) const {
18187       return (+*this).unroll(axis);
18188     }
18189 
18190     //! Rotate an image.
18191     /**
18192        \param angle = rotation angle (in degrees).
18193        \param cond = rotation type. can be :
18194        - 0 = zero-value at borders
18195        - 1 = nearest pixel.
18196        - 2 = cyclic.
18197        \note Returned image will probably have a different size than the instance image *this.
18198     **/
18199     CImg<T>& rotate(const float angle, const unsigned int border_conditions=0, const unsigned int interpolation=1) {
18200       return get_rotate(angle,border_conditions,interpolation).move_to(*this);
18201     }
18202 
18203     CImg<T> get_rotate(const float angle, const unsigned int border_conditions=0, const unsigned int interpolation=1) const {
18204       if (is_empty()) return *this;
18205       CImg<T> res;
18206       const float nangle = cimg::mod(angle,360.0f);
18207       if (border_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles
18208         const int wm1 = width() - 1, hm1 = height() - 1;
18209         const int iangle = (int)nangle/90;
18210         switch (iangle) {
18211         case 1 : {
18212           res.assign(_height,_width,_depth,_spectrum);
18213           T *ptrd = res._data;
18214           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1-x,z,c);
18215         } break;
18216         case 2 : {
18217           res.assign(_width,_height,_depth,_spectrum);
18218           T *ptrd = res._data;
18219           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-x,hm1-y,z,c);
18220         } break;
18221         case 3 : {
18222           res.assign(_height,_width,_depth,_spectrum);
18223           T *ptrd = res._data;
18224           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-y,x,z,c);
18225         } break;
18226         default :
18227           return *this;
18228         }
18229       } else { // generic version
18230         const float
18231           rad = (float)(nangle*cimg::PI/180.0),
18232           ca = (float)std::cos(rad),
18233           sa = (float)std::sin(rad),
18234           ux = cimg::abs(_width*ca), uy = cimg::abs(_width*sa),
18235           vx = cimg::abs(_height*sa), vy = cimg::abs(_height*ca),
18236           w2 = 0.5f*_width, h2 = 0.5f*_height,
18237           dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy);
18238         res.assign((int)(ux+vx),(int)(uy+vy),_depth,_spectrum);
18239         switch (border_conditions) {
18240         case 0 : {
18241           switch (interpolation) {
18242           case 2 : {
18243             T m, M = max_min(m);
18244             cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
18245               const Tfloat val = cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0);
18246               res(x,y,z,c) = (T)(val<m?m:val>M?M:val);
18247             }
18248           } break;
18249           case 1 : {
18250             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18251               res(x,y,z,c) = (T)linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0);
18252           } break;
18253           default : {
18254             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18255               res(x,y,z,c) = atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c,0);
18256           }
18257           }
18258         } break;
18259         case 1 : {
18260           switch (interpolation) {
18261           case 2 : {
18262             T m, M = max_min(m);
18263             cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
18264               const Tfloat val = _cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
18265               res(x,y,z,c) = (T)(val<m?m:val>M?M:val);
18266             }
18267           } break;
18268           case 1 : {
18269             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18270               res(x,y,z,c) = (T)_linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
18271           } break;
18272           default : {
18273             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18274               res(x,y,z,c) = _atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c);
18275           }
18276           }
18277         } break;
18278         case 2 : {
18279           switch (interpolation) {
18280           case 2 : {
18281             T m, M = max_min(m);
18282             cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
18283               const Tfloat val = _cubic_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
18284                                              cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
18285               res(x,y,z,c) = (T)(val<m?m:val>M?M:val);
18286             }
18287           } break;
18288           case 1 : {
18289             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18290               res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
18291                                              cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
18292           } break;
18293           default : {
18294             cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18295               res(x,y,z,c) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),width()),
18296                                       cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),height()),z,c);
18297           }
18298           }
18299         } break;
18300         default :
18301           throw CImgArgumentException(_cimg_instance
18302                                       "rotate() : Invalid specified border conditions %d "
18303                                       "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).",
18304                                       cimg_instance,
18305                                       border_conditions);
18306         }
18307       }
18308       return res;
18309     }
18310 
18311     //! Rotate an image around a center point (\c cx,\c cy).
18312     /**
18313        \param angle = rotation angle (in degrees).
18314        \param cx = X-coordinate of the rotation center.
18315        \param cy = Y-coordinate of the rotation center.
18316        \param zoom = zoom.
18317        \param cond = rotation type. can be :
18318        - 0 = zero-value at borders
18319        - 1 = repeat image at borders
18320        - 2 = zero-value at borders and linear interpolation
18321     **/
18322     CImg<T>& rotate(const float angle, const float cx, const float cy, const float zoom,
18323                     const unsigned int border_conditions=3, const unsigned int interpolation=1) {
18324       return get_rotate(angle,cx,cy,zoom,border_conditions,interpolation).move_to(*this);
18325     }
18326 
18327     CImg<T> get_rotate(const float angle, const float cx, const float cy, const float zoom,
18328                        const unsigned int border_conditions=3, const unsigned int interpolation=1) const {
18329       if (interpolation>2)
18330         throw CImgArgumentException(_cimg_instance
18331                                     "rotate() : Invalid specified interpolation %d "
18332                                     "(should be { 0=none | 1=linear | 2=bicubic }).",
18333                                     cimg_instance,
18334                                     interpolation);
18335 
18336       if (is_empty()) return *this;
18337       CImg<T> res(_width,_height,_depth,_spectrum);
18338       const float
18339         rad = (float)((angle*cimg::PI)/180.0),
18340         ca = (float)std::cos(rad)/zoom,
18341         sa = (float)std::sin(rad)/zoom;
18342       switch (border_conditions) {
18343       case 0 : {
18344         switch (interpolation) {
18345         case 2 : {
18346           T m, M = max_min(m);
18347           cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
18348             const Tfloat val = cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0);
18349             res(x,y,z,c) = (T)(val<m?m:val>M?M:val);
18350           }
18351         } break;
18352         case 1 : {
18353           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18354             res(x,y,z,c) = (T)linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0);
18355         } break;
18356         default : {
18357           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18358             res(x,y,z,c) = atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c,0);
18359         }
18360         }
18361       } break;
18362       case 1 : {
18363         switch (interpolation) {
18364         case 2 : {
18365           T m, M = max_min(m);
18366           cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
18367             const Tfloat val = _cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
18368             res(x,y,z,c) = (T)(val<m?m:val>M?M:val);
18369           }
18370         } break;
18371         case 1 : {
18372           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18373             res(x,y,z,c) = (T)_linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
18374         } break;
18375         default : {
18376           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18377             res(x,y,z,c) = _atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c);
18378         }
18379         }
18380       } break;
18381       case 2 : {
18382         switch (interpolation) {
18383         case 2 : {
18384           T m, M = max_min(m);
18385           cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
18386             const Tfloat val = _cubic_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
18387                                           cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
18388             res(x,y,z,c) = (T)(val<m?m:val>M?M:val);
18389           }
18390         } break;
18391         case 1 : {
18392           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18393             res(x,y,z,c) = (T)_linear_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
18394                                            cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
18395         } break;
18396         default : {
18397           cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
18398             res(x,y,z,c) = (*this)(cimg::mod((int)(cx + (x-cx)*ca + (y-cy)*sa),width()),
18399                                     cimg::mod((int)(cy - (x-cx)*sa + (y-cy)*ca),height()),z,c);
18400         }
18401         }
18402       } break;
18403       default :
18404         throw CImgArgumentException(_cimg_instance
18405                                     "rotate() : Invalid specified border conditions %d "
18406                                     "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).",
18407                                     cimg_instance,
18408                                     border_conditions);
18409       }
18410       return res;
18411     }
18412 
18413     //! Warp an image.
18414     template<typename t>
18415     CImg<T>& warp(const CImg<t>& warp, const bool relative=false,
18416                   const bool interpolation=true, const unsigned int border_conditions=0) {
18417       return get_warp(warp,relative,interpolation,border_conditions).move_to(*this);
18418     }
18419 
18420     template<typename t>
18421     CImg<T> get_warp(const CImg<t>& warp, const bool relative=false,
18422                      const bool interpolation=true, const unsigned int border_conditions=0) const {
18423       if (is_empty() || !warp) return *this;
18424       if (relative && !is_sameXYZ(warp))
18425         throw CImgArgumentException(_cimg_instance
18426                                     "warp() : Instance and specified relative warping field (%u,%u,%u,%u,%p) "
18427                                     "have different XYZ dimensions.",
18428                                     cimg_instance,
18429                                     warp._width,warp._height,warp._depth,warp._spectrum,warp._data);
18430 
18431       CImg<T> res(warp._width,warp._height,warp._depth,_spectrum);
18432       T *ptrd = res._data;
18433       switch (warp._spectrum) {
18434       case 1 : // 1d warping.
18435         if (relative) { // Relative warp coordinates
18436           if (interpolation) switch (border_conditions) {
18437           case 2 : {
18438             cimg_forC(res,c) {
18439               const t *ptrs0 = warp._data;
18440               cimg_forXYZ(res,x,y,z)
18441                 *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
18442             }
18443           } break;
18444           case 1 : {
18445             cimg_forC(res,c) {
18446               const t *ptrs0 = warp._data;
18447               cimg_forXYZ(res,x,y,z)
18448                 *(ptrd++) = (T)_linear_atX(x-(float)*(ptrs0++),y,z,c);
18449             }
18450           } break;
18451           default : {
18452             cimg_forC(res,c) {
18453               const t *ptrs0 = warp._data;
18454               cimg_forXYZ(res,x,y,z)
18455                 *(ptrd++) = (T)linear_atX(x-(float)*(ptrs0++),y,z,c,0);
18456             }
18457           }
18458           } else switch (border_conditions) {
18459           case 2 : {
18460             cimg_forC(res,c) {
18461               const t *ptrs0 = warp._data;
18462               cimg_forXYZ(res,x,y,z)
18463                 *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),y,z,c);
18464             }
18465           } break;
18466           case 1 : {
18467             cimg_forC(res,c) {
18468               const t *ptrs0 = warp._data;
18469               cimg_forXYZ(res,x,y,z)
18470               *(ptrd++) = _atX(x-(int)*(ptrs0++),y,z,c);
18471             }
18472           } break;
18473           default : {
18474             cimg_forC(res,c) {
18475               const t *ptrs0 = warp._data;
18476               cimg_forXYZ(res,x,y,z)
18477                 *(ptrd++) = atX(x-(int)*(ptrs0++),y,z,c,0);
18478             }
18479           }
18480           }
18481         } else { // Absolute warp coordinates
18482           if (interpolation) switch (border_conditions) {
18483           case 2 : {
18484             cimg_forC(res,c) {
18485               const t *ptrs0 = warp._data;
18486               cimg_forXYZ(res,x,y,z)
18487                 *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),y,z,c);
18488             }
18489           } break;
18490           case 1 : {
18491             cimg_forC(res,c) {
18492               const t *ptrs0 = warp._data;
18493               cimg_forXYZ(res,x,y,z)
18494                 *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),y,z,c);
18495             }
18496           } break;
18497           default : {
18498             cimg_forC(res,c) {
18499               const t *ptrs0 = warp._data;
18500               cimg_forXYZ(res,x,y,z)
18501                 *(ptrd++) = (T)linear_atX((float)*(ptrs0++),y,z,c,0);
18502             }
18503           }
18504           } else switch (border_conditions) {
18505           case 2 : {
18506             cimg_forC(res,c) {
18507               const t *ptrs0 = warp._data;
18508               cimg_forXYZ(res,x,y,z)
18509                 *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),y,z,c);
18510             }
18511           } break;
18512           case 1 : {
18513             cimg_forC(res,c) {
18514               const t *ptrs0 = warp._data;
18515               cimg_forXYZ(res,x,y,z)
18516                 *(ptrd++) = _atX((int)*(ptrs0++),y,z,c);
18517             }
18518           } break;
18519           default : {
18520             cimg_forC(res,c) {
18521               const t *ptrs0 = warp._data;
18522               cimg_forXYZ(res,x,y,z)
18523                 *(ptrd++) = atX((int)*(ptrs0++),y,z,c,0);
18524             }
18525           }
18526           }
18527         }
18528         break;
18529 
18530       case 2 : // 2d warping
18531         if (relative) { // Relative warp coordinates
18532           if (interpolation) switch (border_conditions) {
18533           case 2 : {
18534             cimg_forC(res,c) {
18535               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18536               cimg_forXYZ(res,x,y,z)
18537                 *(ptrd++) = (T)_linear_atXY(cimg::mod(x-(float)*(ptrs0++),(float)_width),
18538                                             cimg::mod(y-(float)*(ptrs1++),(float)_height),z,c);
18539             }
18540           } break;
18541           case 1 : {
18542             cimg_forC(res,c) {
18543               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18544               cimg_forXYZ(res,x,y,z)
18545                 *(ptrd++) = (T)_linear_atXY(x-(float)*(ptrs0++),y-(float)*(ptrs1++),z,c);
18546             }
18547           } break;
18548           default : {
18549             cimg_forC(res,c) {
18550               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18551               cimg_forXYZ(res,x,y,z)
18552                 *(ptrd++) = (T)linear_atXY(x-(float)*(ptrs0++),y-(float)*(ptrs1++),z,c,0);
18553             }
18554           }
18555           } else switch (border_conditions) {
18556           case 2 : {
18557             cimg_forC(res,c) {
18558               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18559               cimg_forXYZ(res,x,y,z)
18560                 *(ptrd++) = (*this)(cimg::mod(x-(int)*(ptrs0++),(int)_width),
18561                                     cimg::mod(y-(int)*(ptrs1++),(int)_height),z,c);
18562             }
18563           } break;
18564           case 1 : {
18565             cimg_forC(res,c) {
18566               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18567               cimg_forXYZ(res,x,y,z)
18568                 *(ptrd++) = _atXY(x-(int)*(ptrs0++),y-(int)*(ptrs1++),z,c);
18569             }
18570           } break;
18571           default : {
18572             cimg_forC(res,c) {
18573               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18574               cimg_forXYZ(res,x,y,z)
18575                 *(ptrd++) = atXY(x-(int)*(ptrs0++),y-(int)*(ptrs1++),z,c,0);
18576             }
18577           }
18578           }
18579         } else { // Absolute warp coordinates
18580           if (interpolation) switch (border_conditions) {
18581           case 2 : {
18582             cimg_forC(res,c) {
18583               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18584               cimg_forXYZ(res,x,y,z)
18585                 *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
18586                                             cimg::mod((float)*(ptrs1++),(float)_height),z,c);
18587             }
18588           } break;
18589           case 1 : {
18590             cimg_forC(res,c) {
18591               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18592               cimg_forXYZ(res,x,y,z)
18593                 *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),z,c);
18594             }
18595           } break;
18596           default : {
18597             cimg_forC(res,c) {
18598               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18599               cimg_forXYZ(res,x,y,z)
18600                 *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),z,c,0);
18601             }
18602           }
18603           } else switch (border_conditions) {
18604           case 2 : {
18605             cimg_forC(res,c) {
18606               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18607               cimg_forXYZ(res,x,y,z)
18608                 *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
18609                                     cimg::mod((int)*(ptrs1++),(int)_height),z,c);
18610             }
18611           } break;
18612           case 1 : {
18613             cimg_forC(res,c) {
18614               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18615               cimg_forXYZ(res,x,y,z)
18616                 *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),z,c);
18617             }
18618           } break;
18619           default : {
18620             cimg_forC(res,c) {
18621               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
18622               cimg_forXYZ(res,x,y,z)
18623                 *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),z,c,0);
18624             }
18625           }
18626           }
18627         }
18628         break;
18629 
18630       case 3 : // 3d warping
18631         if (relative) { // Relative warp coordinates
18632           if (interpolation) switch (border_conditions) {
18633           case 2 : {
18634             cimg_forC(res,c) {
18635               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18636               cimg_forXYZ(res,x,y,z)
18637                 *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x-(float)*(ptrs0++),(float)_width),
18638                                              cimg::mod(y-(float)*(ptrs1++),(float)_height),
18639                                              cimg::mod(z-(float)*(ptrs2++),(float)_depth),c);
18640             }
18641           } break;
18642           case 1 : {
18643             cimg_forC(res,c) {
18644               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18645               cimg_forXYZ(res,x,y,z)
18646                 *(ptrd++) = (T)_linear_atXYZ(x-(float)*(ptrs0++),y-(float)*(ptrs1++),z-(float)*(ptrs2++),c);
18647             }
18648           } break;
18649           default : {
18650             cimg_forC(res,c) {
18651               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18652               cimg_forXYZ(res,x,y,z)
18653                 *(ptrd++) = (T)linear_atXYZ(x-(float)*(ptrs0++),y-(float)*(ptrs1++),z-(float)*(ptrs2++),c,0);
18654             }
18655           }
18656           } else switch (border_conditions) {
18657           case 2 : {
18658             cimg_forC(res,c) {
18659               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18660               cimg_forXYZ(res,x,y,z)
18661                 *(ptrd++) = (*this)(cimg::mod(x-(int)*(ptrs0++),(int)_width),
18662                                     cimg::mod(y-(int)*(ptrs1++),(int)_height),
18663                                     cimg::mod(z-(int)*(ptrs2++),(int)_depth),c);
18664             }
18665           } break;
18666           case 1 : {
18667             cimg_forC(res,c) {
18668               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18669               cimg_forXYZ(res,x,y,z)
18670                 *(ptrd++) = _atXYZ(x-(int)*(ptrs0++),y-(int)*(ptrs1++),z-(int)*(ptrs2++),c);
18671             }
18672           } break;
18673           default : {
18674             cimg_forC(res,c) {
18675               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18676               cimg_forXYZ(res,x,y,z)
18677                 *(ptrd++) = atXYZ(x-(int)*(ptrs0++),y-(int)*(ptrs1++),z-(int)*(ptrs2++),c,0);
18678             }
18679           }
18680           }
18681         } else { // Absolute warp coordinates
18682           if (interpolation) switch (border_conditions) {
18683           case 2 : {
18684             cimg_forC(res,c) {
18685               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18686               cimg_forXYZ(res,x,y,z)
18687                 *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
18688                                              cimg::mod((float)*(ptrs1++),(float)_height),
18689                                              cimg::mod((float)*(ptrs2++),(float)_depth),c);
18690             }
18691           } break;
18692           case 1 : {
18693             cimg_forC(res,c) {
18694               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18695               cimg_forXYZ(res,x,y,z)
18696                 *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
18697             }
18698           } break;
18699           default : {
18700             cimg_forC(res,c) {
18701               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18702               cimg_forXYZ(res,x,y,z)
18703                 *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0);
18704             }
18705           }
18706           } else switch (border_conditions) {
18707           case 2 : {
18708             cimg_forC(res,c) {
18709               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18710               cimg_forXYZ(res,x,y,z)
18711                 *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
18712                                     cimg::mod((int)*(ptrs1++),(int)_height),
18713                                     cimg::mod((int)*(ptrs2++),(int)_depth),c);
18714             }
18715           } break;
18716           case 1 : {
18717             cimg_forC(res,c) {
18718               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18719               cimg_forXYZ(res,x,y,z)
18720                 *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c);
18721             }
18722           } break;
18723           default : {
18724             cimg_forC(res,c) {
18725               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
18726               cimg_forXYZ(res,x,y,z)
18727                 *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,0);
18728             }
18729           }
18730           }
18731         }
18732         break;
18733 
18734       default : // 4d warping
18735         if (relative) { // Relative warp coordinates
18736           if (interpolation) switch (border_conditions) {
18737           case 2 : {
18738             cimg_forC(res,c) {
18739               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18740               cimg_forXYZ(res,x,y,z)
18741                 *(ptrd++) = (T)_linear_atXYZC(cimg::mod(x-(float)*(ptrs0++),(float)_width),
18742                                               cimg::mod(y-(float)*(ptrs1++),(float)_height),
18743                                               cimg::mod(z-(float)*(ptrs2++),(float)_depth),
18744                                               cimg::mod(c-(float)*(ptrs3++),(float)_spectrum));
18745             }
18746           } break;
18747           case 1 : {
18748             cimg_forC(res,c) {
18749               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18750               cimg_forXYZ(res,x,y,z)
18751                 *(ptrd++) = (T)_linear_atXYZC(x-(float)*(ptrs0++),y-(float)*(ptrs1++),z-(float)*(ptrs2++),c-(float)*(ptrs3++));
18752             }
18753           } break;
18754           default : {
18755             cimg_forC(res,c) {
18756               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18757               cimg_forXYZ(res,x,y,z)
18758                 *(ptrd++) = (T)linear_atXYZC(x-(float)*(ptrs0++),y-(float)*(ptrs1++),z-(float)*(ptrs2++),c-(float)*(ptrs3++),0);
18759             }
18760           }
18761           } else switch (border_conditions) {
18762           case 2 : {
18763             cimg_forC(res,c) {
18764               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18765               cimg_forXYZ(res,x,y,z)
18766                 *(ptrd++) = (*this)(cimg::mod(x-(int)*(ptrs0++),(int)_width),
18767                                     cimg::mod(y-(int)*(ptrs1++),(int)_height),
18768                                     cimg::mod(z-(int)*(ptrs2++),(int)_depth),
18769                                     cimg::mod(c-(int)*(ptrs3++),(int)_spectrum));
18770             }
18771           } break;
18772           case 1 : {
18773             cimg_forC(res,c) {
18774               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18775               cimg_forXYZ(res,x,y,z)
18776                 *(ptrd++) = _atXYZC(x-(int)*(ptrs0++),y-(int)*(ptrs1++),z-(int)*(ptrs2++),c-(int)*(ptrs3++));
18777             }
18778           } break;
18779           default : {
18780             cimg_forC(res,c) {
18781               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18782               cimg_forXYZ(res,x,y,z)
18783                 *(ptrd++) = atXYZ(x-(int)*(ptrs0++),y-(int)*(ptrs1++),z-(int)*(ptrs2++),c-(int)*(ptrs3++),0);
18784             }
18785           }
18786           }
18787         } else { // Absolute warp coordinates
18788           if (interpolation) switch (border_conditions) {
18789           case 2 : {
18790             cimg_forC(res,c) {
18791               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18792               cimg_forXYZ(res,x,y,z)
18793                 *(ptrd++) = (T)_linear_atXYZC(cimg::mod((float)*(ptrs0++),(float)_width),
18794                                               cimg::mod((float)*(ptrs1++),(float)_height),
18795                                               cimg::mod((float)*(ptrs2++),(float)_depth),
18796                                               cimg::mod((float)*(ptrs3++),(float)_spectrum));
18797             }
18798           } break;
18799           case 1 : {
18800             cimg_forC(res,c) {
18801               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18802               cimg_forXYZ(res,x,y,z)
18803                 *(ptrd++) = (T)_linear_atXYZC((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),(float)*(ptrs3++));
18804             }
18805           } break;
18806           default : {
18807             cimg_forC(res,c) {
18808               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18809               cimg_forXYZ(res,x,y,z)
18810                 *(ptrd++) = (T)linear_atXYZC((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),(float)*(ptrs3++),0);
18811             }
18812           }
18813           } else switch (border_conditions) {
18814           case 2 : {
18815             cimg_forC(res,c) {
18816               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18817               cimg_forXYZ(res,x,y,z)
18818                 *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
18819                                     cimg::mod((int)*(ptrs1++),(int)_height),
18820                                     cimg::mod((int)*(ptrs2++),(int)_depth),
18821                                     cimg::mod((int)*(ptrs3++),(int)_spectrum));
18822             }
18823           } break;
18824           case 1 : {
18825             cimg_forC(res,c) {
18826               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18827               cimg_forXYZ(res,x,y,z)
18828                 *(ptrd++) = _atXYZC((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),(int)*(ptrs3++));
18829             }
18830           } break;
18831           default : {
18832             cimg_forC(res,c) {
18833               const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2), *ptrs3 = warp.data(0,0,0,3);
18834               cimg_forXYZ(res,x,y,z)
18835                 *(ptrd++) = atXYZC((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),(int)*(ptrs3++),0);
18836             }
18837           }
18838           }
18839         }
18840       }
18841       return res;
18842     }
18843 
18844     //! Return a 2d representation of a 3d image, with three slices.
18845     CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0,
18846                            const int dx=-100, const int dy=-100, const int dz=-100) {
18847       return get_projections2d(x0,y0,z0,dx,dy,dz).move_to(*this);
18848     }
18849 
18850     CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0,
18851                               const int dx=-100, const int dy=-100, const int dz=-100) const {
18852       if (is_empty()) return *this;
18853       const unsigned int
18854         nx0 = (x0>=_width)?_width - 1:x0,
18855         ny0 = (y0>=_height)?_height - 1:y0,
18856         nz0 = (z0>=_depth)?_depth - 1:z0;
18857       CImg<T>
18858         imgxy(_width,_height,1,_spectrum),
18859         imgzy(_depth,_height,1,_spectrum),
18860         imgxz(_width,_depth,1,_spectrum);
18861       cimg_forXYC(*this,x,y,c) imgxy(x,y,c) = (*this)(x,y,nz0,c);
18862       cimg_forYZC(*this,y,z,c) imgzy(z,y,c) = (*this)(nx0,y,z,c);
18863       cimg_forXZC(*this,x,z,c) imgxz(x,z,c) = (*this)(x,ny0,z,c);
18864       imgxy.resize(dx,dy,1,_spectrum,1);
18865       imgzy.resize(dz,dy,1,_spectrum,1);
18866       imgxz.resize(dx,dz,1,_spectrum,1);
18867       return CImg<T>(imgxy._width + imgzy._width,imgxy._height + imgxz._height,1,_spectrum,cimg::min(imgxy.min(),imgzy.min(),imgxz.min())).
18868         draw_image(imgxy).draw_image(imgxy._width,imgzy).draw_image(0,imgxy._height,imgxz);
18869     }
18870 
18871     //! Get a square region of the image.
18872     /**
18873        \param x0 = X-coordinate of the upper-left crop rectangle corner.
18874        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
18875        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
18876        \param c0 = C-coordinate of the upper-left crop rectangle corner.
18877        \param x1 = X-coordinate of the lower-right crop rectangle corner.
18878        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
18879        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
18880        \param c1 = C-coordinate of the lower-right crop rectangle corner.
18881        \param border_condition = Dirichlet (false) or Neumann border conditions.
18882     **/
18883     CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
18884                   const int x1, const int y1, const int z1, const int c1,
18885                   const bool border_condition=false) {
18886       return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,border_condition).move_to(*this);
18887     }
18888 
18889     CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
18890                      const int x1, const int y1, const int z1, const int c1,
18891                      const bool border_condition=false) const {
18892       if (is_empty()) return *this;
18893       const int
18894         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
18895         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
18896         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
18897         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
18898       CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
18899       if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) {
18900         if (border_condition) cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0+x,ny0+y,nz0+z,nc0+c);
18901         else res.fill(0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
18902       } else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
18903       return res;
18904     }
18905 
18906     //! Get a rectangular part of the instance image.
18907     /**
18908        \param x0 = X-coordinate of the upper-left crop rectangle corner.
18909        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
18910        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
18911        \param x1 = X-coordinate of the lower-right crop rectangle corner.
18912        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
18913        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
18914        \param border_condition = determine the type of border condition if
18915        some of the desired region is outside the image.
18916     **/
18917     CImg<T>& crop(const int x0, const int y0, const int z0,
18918                   const int x1, const int y1, const int z1,
18919                   const bool border_condition=false) {
18920       return crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,border_condition);
18921     }
18922 
18923     CImg<T> get_crop(const int x0, const int y0, const int z0,
18924                      const int x1, const int y1, const int z1,
18925                      const bool border_condition=false) const {
18926       return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,border_condition);
18927     }
18928 
18929     //! Get a rectangular part of the instance image.
18930     /**
18931        \param x0 = X-coordinate of the upper-left crop rectangle corner.
18932        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
18933        \param x1 = X-coordinate of the lower-right crop rectangle corner.
18934        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
18935        \param border_condition = determine the type of border condition if
18936        some of the desired region is outside the image.
18937     **/
18938     CImg<T>& crop(const int x0, const int y0,
18939                   const int x1, const int y1,
18940                   const bool border_condition=false) {
18941       return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,border_condition);
18942     }
18943 
18944     CImg<T> get_crop(const int x0, const int y0,
18945                      const int x1, const int y1,
18946                      const bool border_condition=false) const {
18947       return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,border_condition);
18948     }
18949 
18950     //! Get a rectangular part of the instance image.
18951     /**
18952        \param x0 = X-coordinate of the upper-left crop rectangle corner.
18953        \param x1 = X-coordinate of the lower-right crop rectangle corner.
18954        \param border_condition = determine the type of border condition if
18955        some of the desired region is outside the image.
18956     **/
18957     CImg<T>& crop(const int x0, const int x1, const bool border_condition=false) {
18958       return crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,border_condition);
18959     }
18960 
18961     CImg<T> get_crop(const int x0, const int x1, const bool border_condition=false) const {
18962       return get_crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,border_condition);
18963     }
18964 
18965     //! Autocrop an image, regarding of the specified backround value.
18966     CImg<T>& autocrop(const T value, const char *const axes="czyx") {
18967       if (is_empty()) return *this;
18968       for (const char *s = axes; *s; ++s) {
18969         const char axis = cimg::uncase(*s);
18970         const CImg<intT> coords = _autocrop(value,axis);
18971         switch (axis) {
18972         case 'x' : {
18973           const int x0 = coords[0], x1 = coords[1];
18974           if (x0>=0 && x1>=0) crop(x0,x1);
18975         } break;
18976         case 'y' : {
18977           const int y0 = coords[0], y1 = coords[1];
18978           if (y0>=0 && y1>=0) crop(0,y0,_width-1,y1);
18979         } break;
18980         case 'z' : {
18981           const int z0 = coords[0], z1 = coords[1];
18982           if (z0>=0 && z1>=0) crop(0,0,z0,_width-1,_height-1,z1);
18983         } break;
18984         default : {
18985           const int c0 = coords[0], c1 = coords[1];
18986           if (c0>=0 && c1>=0) crop(0,0,0,c0,_width-1,_height-1,_depth-1,c1);
18987         }
18988         }
18989       }
18990       return *this;
18991     }
18992 
18993     CImg<T> get_autocrop(const T value, const char *const axes="czyx") const {
18994       return (+*this).autocrop(value,axes);
18995     }
18996 
18997     //! Autocrop an image, regarding of the specified backround color.
18998     CImg<T>& autocrop(const T *const color, const char *const axes="zyx") {
18999       if (is_empty()) return *this;
19000       for (const char *s = axes; *s; ++s) {
19001         const char axis = cimg::uncase(*s);
19002         switch (axis) {
19003         case 'x' : {
19004           int x0 = _width, x1 = -1;
19005           cimg_forC(*this,c) {
19006             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
19007             const int nx0 = coords[0], nx1 = coords[1];
19008             if (nx0>=0 && nx1>=0) { x0 = cimg::min(x0,nx0); x1 = cimg::max(x1,nx1); }
19009           }
19010           if (x0<=x1) crop(x0,x1);
19011         } break;
19012         case 'y' : {
19013           int y0 = _height, y1 = -1;
19014           cimg_forC(*this,c) {
19015             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
19016             const int ny0 = coords[0], ny1 = coords[1];
19017             if (ny0>=0 && ny1>=0) { y0 = cimg::min(y0,ny0); y1 = cimg::max(y1,ny1); }
19018           }
19019           if (y0<=y1) crop(0,y0,_width-1,y1);
19020         } break;
19021         default : {
19022           int z0 = _depth, z1 = -1;
19023           cimg_forC(*this,c) {
19024             const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
19025             const int nz0 = coords[0], nz1 = coords[1];
19026             if (nz0>=0 && nz1>=0) { z0 = cimg::min(z0,nz0); z1 = cimg::max(z1,nz1); }
19027           }
19028           if (z0<=z1) crop(0,0,z0,_width-1,_height-1,z1);
19029         }
19030         }
19031       }
19032       return *this;
19033     }
19034 
19035     CImg<T> get_autocrop(const T *const color, const char *const axes="zyx") const {
19036       return (+*this).autocrop(color,axes);
19037     }
19038 
19039     //! Autocrop an image, regarding of the specified backround color.
19040     template<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") {
19041       return get_autocrop(color,axes).move_to(*this);
19042     }
19043 
19044     template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char *const axes="zyx") const {
19045       return get_autocrop(color._data,axes);
19046     }
19047 
19048     CImg<intT> _autocrop(const T value, const char axis) const {
19049       CImg<intT> res;
19050       int x0 = -1, y0 = -1, z0 = -1, c0 = -1, x1 = -1, y1 = -1, z1 = -1, c1 = -1;
19051       switch (cimg::uncase(axis)) {
19052       case 'x' : {
19053         cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
19054           if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
19055         if (x0>=0) {
19056           for (int x = width()-1; x>=0; --x) cimg_forYZC(*this,y,z,c)
19057             if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
19058         }
19059         res = CImg<intT>::vector(x0,x1);
19060       } break;
19061       case 'y' : {
19062         cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
19063           if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
19064         if (y0>=0) {
19065           for (int y = height()-1; y>=0; --y) cimg_forXZC(*this,x,z,c)
19066             if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
19067         }
19068         res = CImg<intT>::vector(y0,y1);
19069       } break;
19070       case 'z' : {
19071         cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
19072           if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
19073         if (z0>=0) {
19074           for (int z = depth()-1; z>=0; --z) cimg_forXYC(*this,x,y,c)
19075             if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
19076         }
19077         res = CImg<intT>::vector(z0,z1);
19078       } break;
19079       default : {
19080         cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
19081           if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
19082         if (c0>=0) {
19083           for (int c = spectrum()-1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
19084             if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
19085         }
19086         res = CImg<intT>::vector(c0,c1);
19087       }
19088       }
19089       return res;
19090     }
19091 
19092     //! Get one column.
19093     CImg<T>& column(const unsigned int x0) {
19094       return columns(x0,x0);
19095     }
19096 
19097     CImg<T> get_column(const unsigned int x0) const {
19098       return get_columns(x0,x0);
19099     }
19100 
19101     //! Get a set of columns.
19102     CImg<T>& columns(const unsigned int x0, const unsigned int x1) {
19103       return get_columns(x0,x1).move_to(*this);
19104     }
19105 
19106     CImg<T> get_columns(const unsigned int x0, const unsigned int x1) const {
19107       return get_crop((int)x0,0,0,0,(int)x1,height()-1,depth()-1,spectrum()-1);
19108     }
19109 
19110     //! Get a line.
19111     CImg<T>& line(const unsigned int y0) {
19112       return lines(y0,y0);
19113     }
19114 
19115     CImg<T> get_line(const unsigned int y0) const {
19116       return get_lines(y0,y0);
19117     }
19118 
19119     //! Get a set of lines.
19120     CImg<T>& lines(const unsigned int y0, const unsigned int y1) {
19121       return get_lines(y0,y1).move_to(*this);
19122     }
19123 
19124     CImg<T> get_lines(const unsigned int y0, const unsigned int y1) const {
19125       return get_crop(0,(int)y0,0,0,width()-1,(int)y1,depth()-1,spectrum()-1);
19126     }
19127 
19128     //! Get a slice.
19129     CImg<T>& slice(const unsigned int z0) {
19130       return slices(z0,z0);
19131     }
19132 
19133     CImg<T> get_slice(const unsigned int z0) const {
19134       return get_slices(z0,z0);
19135     }
19136 
19137     //! Get a set of slices.
19138     CImg<T>& slices(const unsigned int z0, const unsigned int z1) {
19139       return get_slices(z0,z1).move_to(*this);
19140     }
19141 
19142     CImg<T> get_slices(const unsigned int z0, const unsigned int z1) const {
19143       return get_crop(0,0,(int)z0,0,width()-1,height()-1,(int)z1,spectrum()-1);
19144     }
19145 
19146     //! Get a channel.
19147     CImg<T>& channel(const unsigned int c0) {
19148       return channels(c0,c0);
19149     }
19150 
19151     CImg<T> get_channel(const unsigned int c0) const {
19152       return get_channels(c0,c0);
19153     }
19154 
19155     //! Get a set of channels.
19156     CImg<T>& channels(const unsigned int c0, const unsigned int c1) {
19157       return get_channels(c0,c1).move_to(*this);
19158     }
19159 
19160     CImg<T> get_channels(const unsigned int c0, const unsigned int c1) const {
19161       return get_crop(0,0,0,(int)c0,width()-1,height()-1,depth()-1,(int)c1);
19162     }
19163 
19164     //! Get a shared-memory image referencing a set of points of the instance image.
19165     CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
19166                               const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
19167       const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
19168       if (beg>end || beg>=size() || end>=size())
19169         throw CImgArgumentException(_cimg_instance
19170                                     "get_shared_points() : Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
19171                                     cimg_instance,
19172                                     x0,x1,y0,z0,c0);
19173 
19174       return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
19175     }
19176 
19177     const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
19178                                     const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
19179       const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
19180       if (beg>end || beg>=size() || end>=size())
19181         throw CImgArgumentException(_cimg_instance
19182                                     "get_shared_points() : Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
19183                                     cimg_instance,
19184                                     x0,x1,y0,z0,c0);
19185 
19186       return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
19187     }
19188 
19189     //! Return a shared-memory image referencing a set of lines of the instance image.
19190     CImg<T> get_shared_lines(const unsigned int y0, const unsigned int y1,
19191                              const unsigned int z0=0, const unsigned int c0=0) {
19192       const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
19193       if (beg>end || beg>=size() || end>=size())
19194         throw CImgArgumentException(_cimg_instance
19195                                     "get_shared_lines() : Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).",
19196                                     cimg_instance,
19197                                     _width-1,y0,y1,z0,c0);
19198 
19199       return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
19200     }
19201 
19202     const CImg<T> get_shared_lines(const unsigned int y0, const unsigned int y1,
19203                                    const unsigned int z0=0, const unsigned int c0=0) const {
19204       const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
19205       if (beg>end || beg>=size() || end>=size())
19206         throw CImgArgumentException(_cimg_instance
19207                                     "get_shared_lines() : Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).",
19208                                     cimg_instance,
19209                                     _width-1,y0,y1,z0,c0);
19210 
19211       return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
19212     }
19213 
19214     //! Return a shared-memory image referencing one particular line (y0,z0,c0) of the instance image.
19215     CImg<T> get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
19216       return get_shared_lines(y0,y0,z0,c0);
19217     }
19218 
19219     const CImg<T> get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
19220       return get_shared_lines(y0,y0,z0,c0);
19221     }
19222 
19223     //! Return a shared memory image referencing a set of planes (z0->z1,c0) of the instance image.
19224     CImg<T> get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
19225       const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
19226       if (beg>end || beg>=size() || end>=size())
19227         throw CImgArgumentException(_cimg_instance
19228                                     "get_shared_planes() : Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).",
19229                                     cimg_instance,
19230                                     _width-1,_height-1,z0,z1,c0);
19231 
19232       return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
19233     }
19234 
19235     const CImg<T> get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
19236       const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
19237       if (beg>end || beg>=size() || end>=size())
19238         throw CImgArgumentException(_cimg_instance
19239                                     "get_shared_planes() : Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).",
19240                                     cimg_instance,
19241                                     _width-1,_height-1,z0,z1,c0);
19242 
19243       return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
19244     }
19245 
19246     //! Return a shared-memory image referencing one plane (z0,c0) of the instance image.
19247     CImg<T> get_shared_plane(const unsigned int z0, const unsigned int c0=0) {
19248       return get_shared_planes(z0,z0,c0);
19249     }
19250 
19251     const CImg<T> get_shared_plane(const unsigned int z0, const unsigned int c0=0) const {
19252       return get_shared_planes(z0,z0,c0);
19253     }
19254 
19255     //! Return a shared-memory image referencing a set of channels (c0->c1) of the instance image.
19256     CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
19257       const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
19258       if (beg>end || beg>=size() || end>=size())
19259         throw CImgArgumentException(_cimg_instance
19260                                     "get_shared_channels() : Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).",
19261                                     cimg_instance,
19262                                     _width-1,_height-1,_depth-1,c0,c1);
19263 
19264       return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
19265     }
19266 
19267     const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
19268       const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
19269       if (beg>end || beg>=size() || end>=size())
19270         throw CImgArgumentException(_cimg_instance
19271                                     "get_shared_channels() : Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).",
19272                                     cimg_instance,
19273                                     _width-1,_height-1,_depth-1,c0,c1);
19274 
19275       return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
19276     }
19277 
19278     //! Return a shared-memory image referencing one channel c0 of the instance image.
19279     CImg<T> get_shared_channel(const unsigned int c0) {
19280       return get_shared_channels(c0,c0);
19281     }
19282 
19283     const CImg<T> get_shared_channel(const unsigned int c0) const {
19284       return get_shared_channels(c0,c0);
19285     }
19286 
19287     //! Split image into a list.
19288     CImgList<T> get_split(const char axis, const int nb=0) const {
19289       CImgList<T> res;
19290       const char naxis = cimg::uncase(axis);
19291       const unsigned int nnb = (unsigned int)(nb==0?1:(nb>0?nb:-nb));
19292       if (nb<=0) switch (naxis) { // Split by bloc size
19293       case 'x': {
19294         for (unsigned int p = 0; p<_width; p+=nnb) get_crop(p,0,0,0,cimg::min(p+nnb-1,_width-1),_height-1,_depth-1,_spectrum-1).move_to(res);
19295       } break;
19296       case 'y': {
19297         for (unsigned int p = 0; p<_height; p+=nnb) get_crop(0,p,0,0,_width-1,cimg::min(p+nnb-1,_height-1),_depth-1,_spectrum-1).move_to(res);
19298       } break;
19299       case 'z': {
19300         for (unsigned int p = 0; p<_depth; p+=nnb) get_crop(0,0,p,0,_width-1,_height-1,cimg::min(p+nnb-1,_depth-1),_spectrum-1).move_to(res);
19301       } break;
19302       default: {
19303         for (unsigned int p = 0; p<_spectrum; p+=nnb) get_crop(0,0,0,p,_width-1,_height-1,_depth-1,cimg::min(p+nnb-1,_spectrum-1)).move_to(res);
19304       }
19305       } else { // Split by number of blocs
19306         const unsigned int siz = naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:naxis=='c'?_spectrum:0;
19307         if (nnb>siz)
19308           throw CImgArgumentException(_cimg_instance
19309                                       "get_split() : Instance cannot be split along %c-axis into %u blocs.",
19310                                       cimg_instance,
19311                                       axis,nnb);
19312         res.assign(nnb);
19313         switch (naxis) {
19314         case 'x' : {
19315           cimglist_for(res,p) get_crop(p*siz/nnb,0,0,0,(p+1)*siz/nnb-1,_height-1,_depth-1,_spectrum-1).move_to(res[p]);
19316         } break;
19317         case 'y' : {
19318           cimglist_for(res,p) get_crop(0,p*siz/nnb,0,0,_width-1,(p+1)*siz/nnb-1,_depth-1,_spectrum-1).move_to(res[p]);
19319         } break;
19320         case 'z' : {
19321           cimglist_for(res,p) get_crop(0,0,p*siz/nnb,0,_width-1,_height-1,(p+1)*siz/nnb-1,_spectrum-1).move_to(res[p]);
19322         } break;
19323         default : {
19324           cimglist_for(res,p) get_crop(0,0,0,p*siz/nnb,_width-1,_height-1,_depth-1,(p+1)*siz/nnb-1).move_to(res[p]);
19325         }
19326         }
19327       }
19328       return res;
19329     }
19330 
19331     // Split image into a list of vectors, according to a given splitting value.
19332     CImgList<T> get_split(const T value, const bool keep_values, const bool shared, const char axis='y') const {
19333       CImgList<T> res;
19334       const T *ptr0 = _data, *const ptre = _data + size();
19335       while (ptr0<ptre) {
19336         const T *ptr1 = ptr0;
19337         while (ptr1<ptre && *ptr1==value) ++ptr1;
19338         const unsigned int siz0 = ptr1 - ptr0;
19339         if (siz0 && keep_values) res.insert(CImg<T>(ptr0,1,siz0,1,1,shared));
19340         ptr0 = ptr1;
19341         while (ptr1<ptre && *ptr1!=value) ++ptr1;
19342         const unsigned int siz1 = ptr1 - ptr0;
19343         if (siz1) res.insert(CImg<T>(ptr0,1,siz1,1,1,shared),~0U,shared);
19344         ptr0 = ptr1;
19345       }
19346       cimglist_apply(res,unroll)(axis);
19347       return res;
19348     }
19349 
19350     //! Append an image.
19351     template<typename t>
19352     CImg<T>& append(const CImg<t>& img, const char axis='x', const char align='p') {
19353       if (is_empty()) return assign(img,false);
19354       if (!img) return *this;
19355       return CImgList<T>(*this,img).get_append(axis,align).move_to(*this);
19356     }
19357 
19358     CImg<T>& append(const CImg<T>& img, const char axis='x', const char align='p') {
19359       if (is_empty()) return assign(img,false);
19360       if (!img) return *this;
19361       return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
19362     }
19363 
19364     template<typename t>
19365     CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const char align='p') const {
19366       if (is_empty()) return +img;
19367       if (!img) return +*this;
19368       return CImgList<_cimg_Tt>(*this,img).get_append(axis,align);
19369     }
19370 
19371     CImg<T> get_append(const CImg<T>& img, const char axis='x', const char align='p') const {
19372       if (is_empty()) return +img;
19373       if (!img) return +*this;
19374       return CImgList<T>(*this,img,true).get_append(axis,align);
19375     }
19376 
19377     //@}
19378     //---------------------------------------
19379     //
19380     //! \name Filtering / Transforms
19381     //@{
19382     //---------------------------------------
19383 
19384     //! Compute the correlation of the instance image by a mask.
19385     /**
19386        The correlation of the instance image \p *this by the mask \p mask is defined to be :
19387 
19388        res(x,y,z) = sum_{i,j,k} (*this)(x+i,y+j,z+k)*mask(i,j,k)
19389 
19390        \param mask = the correlation kernel.
19391        \param cond = the border condition type (0=zero, 1=dirichlet)
19392        \param weighted_correl = enable local normalization.
19393     **/
19394     template<typename t>
19395     CImg<T>& correlate(const CImg<t>& mask, const unsigned int cond=1, const bool weighted_correl=false) {
19396       return get_correlate(mask,cond,weighted_correl).move_to(*this);
19397     }
19398 
19399     template<typename t>
19400     CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& mask, const unsigned int cond=1,
19401                                       const bool weighted_correl=false) const {
19402       if (!mask || mask._spectrum!=1)
19403         throw CImgArgumentException(_cimg_instance
19404                                     "correlate() : Specified mask (%u,%u,%u,%u,%p) is not scalar.",
19405                                     cimg_instance,
19406                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
19407 
19408       if (is_empty()) return *this;
19409       typedef _cimg_Ttfloat Ttfloat;
19410       CImg<Ttfloat> res(_width,_height,_depth,_spectrum);
19411       Ttfloat *ptrd = res._data;
19412       if (cond && mask._width==mask._height && ((mask._depth==1 && mask._width<=5) || (mask._depth==mask._width && mask._width<=3))) {
19413         // A special optimization is done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 mask (with cond=1)
19414         switch (mask._depth) {
19415         case 3 : {
19416           T I[27] = { 0 };
19417           cimg_forZC(*this,z,c) cimg_for3x3x3(*this,x,y,z,c,I,T) *(ptrd++) = (Ttfloat)
19418             (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] +
19419              I[ 3]*mask[ 3] + I[ 4]*mask[ 4] + I[ 5]*mask[ 5] +
19420              I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] +
19421              I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] +
19422              I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] +
19423              I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] +
19424              I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] +
19425              I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] +
19426              I[24]*mask[24] + I[25]*mask[25] + I[26]*mask[26]);
19427           ptrd = res._data;
19428           if (weighted_correl) cimg_forZC(*this,z,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
19429             const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] +
19430                                            I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
19431                                            I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] +
19432                                            I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
19433                                            I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
19434                                            I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
19435                                            I[18]*I[18] + I[19]*I[19] + I[20]*I[20] +
19436                                            I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
19437                                            I[24]*I[24] + I[25]*I[25] + I[26]*I[26]);
19438             if (weight>0) *ptrd/=(Ttfloat)std::sqrt(weight);
19439             ++ptrd;
19440           }
19441         } break;
19442         case 2 : {
19443           T I[8] = { 0 };
19444           cimg_forZC(*this,z,c) cimg_for2x2x2(*this,x,y,z,c,I,T) *(ptrd++) = (Ttfloat)
19445             (I[0]*mask[0] + I[1]*mask[1] +
19446              I[2]*mask[2] + I[3]*mask[3] +
19447              I[4]*mask[4] + I[5]*mask[5] +
19448              I[6]*mask[6] + I[7]*mask[7]);
19449           ptrd = res._data;
19450           if (weighted_correl) cimg_forZC(*this,z,c) cimg_for2x2x2(*this,x,y,z,c,I,T) {
19451             const double weight = (double)(I[0]*I[0] + I[1]*I[1] +
19452                                            I[2]*I[2] + I[3]*I[3] +
19453                                            I[4]*I[4] + I[5]*I[5] +
19454                                            I[6]*I[6] + I[7]*I[7]);
19455             if (weight>0) *ptrd/=(Ttfloat)std::sqrt(weight);
19456             ++ptrd;
19457           }
19458         } break;
19459         default :
19460         case 1 :
19461           switch (mask._width) {
19462           case 6 : {
19463             T I[36] = { 0 };
19464             cimg_forZC(*this,z,c) cimg_for6x6(*this,x,y,z,c,I,T) *(ptrd++) = (Ttfloat)
19465               (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] + I[ 4]*mask[ 4] + I[ 5]*mask[ 5] +
19466                I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] +
19467                I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] +
19468                I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] +
19469                I[24]*mask[24] + I[25]*mask[25] + I[26]*mask[26] + I[27]*mask[27] + I[28]*mask[28] + I[29]*mask[29] +
19470                I[30]*mask[30] + I[31]*mask[31] + I[32]*mask[32] + I[33]*mask[33] + I[34]*mask[34] + I[35]*mask[35]);
19471             ptrd = res._data;
19472             if (weighted_correl) cimg_forZC(*this,z,c) cimg_for6x6(*this,x,y,z,c,I,T) {
19473               const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
19474                                              I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
19475                                              I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
19476                                              I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
19477                                              I[24]*I[24] + I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] +
19478                                              I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + I[35]*I[35]);
19479               if (weight>0) *ptrd/=(Ttfloat)std::sqrt(weight);
19480               ++ptrd;
19481             }
19482           } break;
19483           case 5 : {
19484             T I[25] = { 0 };
19485             cimg_forZC(*this,z,c) cimg_for5x5(*this,x,y,z,c,I,T) *(ptrd++) = (Ttfloat)
19486               (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] + I[ 4]*mask[ 4] +
19487                I[ 5]*mask[ 5] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] +
19488                I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] +
19489                I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + I[18]*mask[18] + I[19]*mask[19] +
19490                I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + I[24]*mask[24]);
19491             ptrd = res._data;
19492             if (weighted_correl) cimg_forZC(*this,z,c) cimg_for5x5(*this,x,y,z,c,I,T) {
19493               const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
19494                                              I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
19495                                              I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
19496                                              I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
19497                                              I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]);
19498               if (weight>0) *ptrd/=(Ttfloat)std::sqrt(weight);
19499               ++ptrd;
19500             }
19501           } break;
19502           case 4 : {
19503             T I[16] = { 0 };
19504             cimg_forZC(*this,z,c) cimg_for4x4(*this,x,y,z,c,I,T) *(ptrd++) = (Ttfloat)
19505               (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] +
19506                I[ 4]*mask[ 4] + I[ 5]*mask[ 5] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] +
19507                I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] +
19508                I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15]);
19509             ptrd = res._data;
19510             if (weighted_correl) cimg_forZC(*this,z,c) cimg_for4x4(*this,x,y,z,c,I,T) {
19511               const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] +
19512                                              I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] +
19513                                              I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
19514                                              I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]);
19515               if (weight>0) *ptrd/=(Ttfloat)std::sqrt(weight);
19516               ++ptrd;
19517             }
19518           } break;
19519           case 3 : {
19520             T I[9] = { 0 };
19521             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T) *(ptrd++) = (Ttfloat)
19522               (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] +
19523                I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] +
19524                I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]);
19525             ptrd = res._data;
19526             if (weighted_correl) cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T) {
19527               const double weight = (double)(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] +
19528                                              I[3]*I[3] + I[4]*I[4] + I[5]*I[5] +
19529                                              I[6]*I[6] + I[7]*I[7] + I[8]*I[8]);
19530               if (weight>0) *ptrd/=(Ttfloat)std::sqrt(weight);
19531               ++ptrd;
19532             }
19533           } break;
19534           case 2 : {
19535             T I[4] = { 0 };
19536             cimg_forZC(*this,z,c) cimg_for2x2(*this,x,y,z,c,I,T) *(ptrd++) = (Ttfloat)
19537               (I[0]*mask[0] + I[1]*mask[1] +
19538                I[2]*mask[2] + I[3]*mask[3]);
19539             ptrd = res._data;
19540             if (weighted_correl) cimg_forZC(*this,z,c) cimg_for2x2(*this,x,y,z,c,I,T) {
19541               const double weight = (double)(I[0]*I[0] + I[1]*I[1] +
19542                                              I[2]*I[2] + I[3]*I[3]);
19543               if (weight>0) *ptrd/=(Ttfloat)std::sqrt(weight);
19544               ++ptrd;
19545             }
19546           } break;
19547           case 1 : (res.assign(*this))*=mask(0); break;
19548           }
19549         }
19550       } else { // Generic version for other masks
19551         const int
19552           mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
19553           mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
19554           mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
19555         cimg_forC(*this,c)
19556           if (!weighted_correl) { // Classical correlation
19557             for (int z = mz1; z<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
19558               Ttfloat val = 0;
19559               for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm)
19560                 val+=(*this)(x+xm,y+ym,z+zm,c)*mask(mx1+xm,my1+ym,mz1+zm);
19561               res(x,y,z,c) = (Ttfloat)val;
19562             }
19563             if (cond)
19564               cimg_forYZC(*this,y,z,c)
19565                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19566                   Ttfloat val = 0;
19567                   for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm)
19568                     val+=_atXYZ(x+xm,y+ym,z+zm,c)*mask(mx1+xm,my1+ym,mz1+zm);
19569                   res(x,y,z,c) = (Ttfloat)val;
19570                 }
19571             else
19572               cimg_forYZC(*this,y,z,c)
19573                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19574                   Ttfloat val = 0;
19575                   for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm)
19576                     val+=atXYZ(x+xm,y+ym,z+zm,c,0)*mask(mx1+xm,my1+ym,mz1+zm);
19577                   res(x,y,z,c) = (Ttfloat)val;
19578                 }
19579           } else { // Weighted correlation
19580             for (int z = mz1; z<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
19581               Ttfloat val = 0, weight = 0;
19582               for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19583                 const Ttfloat cval = (Ttfloat)(*this)(x+xm,y+ym,z+zm,c);
19584                 val+=cval*mask(mx1+xm,my1+ym,mz1+zm);
19585                 weight+=cval*cval;
19586               }
19587               res(x,y,z,c) = (weight>(Ttfloat)0)?(Ttfloat)(val/std::sqrt((double)weight)):(Ttfloat)0;
19588             }
19589             if (cond)
19590               cimg_forYZC(*this,y,z,c)
19591                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19592                   Ttfloat val = 0, weight = 0;
19593                   for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19594                     const Ttfloat cval = (Ttfloat)_atXYZ(x+xm,y+ym,z+zm,c);
19595                     val+=cval*mask(mx1+xm,my1+ym,mz1+zm);
19596                     weight+=cval*cval;
19597                   }
19598                   res(x,y,z,c) = (weight>(Ttfloat)0)?(Ttfloat)(val/std::sqrt((double)weight)):(Ttfloat)0;
19599                 }
19600             else
19601               cimg_forYZC(*this,y,z,c)
19602                 for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19603                   Ttfloat val = 0, weight = 0;
19604                   for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19605                     const Ttfloat cval = (Ttfloat)atXYZ(x+xm,y+ym,z+zm,c,0);
19606                     val+=cval*mask(mx1+xm,my1+ym,mz1+zm);
19607                     weight+=cval*cval;
19608                   }
19609                   res(x,y,z,c) = (weight>(Ttfloat)0)?(Ttfloat)(val/std::sqrt((double)weight)):(Ttfloat)0;
19610                 }
19611           }
19612       }
19613       return res;
19614     }
19615 
19616     //! Compute the convolution of the image by a mask.
19617     /**
19618        The result \p res of the convolution of an image \p img by a mask \p mask is defined to be :
19619 
19620        res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*mask(i,j,k)
19621 
19622        \param mask = the correlation kernel.
19623        \param cond = the border condition type (0=zero, 1=dirichlet)
19624        \param weighted_convol = enable local normalization.
19625     **/
19626     template<typename t>
19627     CImg<T>& convolve(const CImg<t>& mask, const unsigned int cond=1, const bool weighted_convol=false) {
19628       return get_convolve(mask,cond,weighted_convol).move_to(*this);
19629     }
19630 
19631     template<typename t>
19632     CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& mask, const unsigned int cond=1,
19633                                      const bool weighted_convol=false) const {
19634       if (!mask || mask._spectrum!=1)
19635         throw CImgArgumentException(_cimg_instance
19636                                     "convolve() : Specified mask (%u,%u,%u,%u,%p) is not scalar.",
19637                                     cimg_instance,
19638                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
19639 
19640       if (is_empty()) return *this;
19641       return get_correlate(CImg<t>(mask._data,mask.size(),1,1,1,true).get_mirror('x').resize(mask,-1),cond,weighted_convol);
19642     }
19643 
19644     //! Return the erosion of the image by a structuring element.
19645     template<typename t>
19646     CImg<T>& erode(const CImg<t>& mask, const unsigned int cond=1, const bool weighted_erosion=false) {
19647       return get_erode(mask,cond,weighted_erosion).move_to(*this);
19648     }
19649 
19650     template<typename t>
19651     CImg<_cimg_Tt> get_erode(const CImg<t>& mask, const unsigned int cond=1,
19652                              const bool weighted_erosion=false) const {
19653       if (!mask || mask._spectrum!=1)
19654         throw CImgArgumentException(_cimg_instance
19655                                     "erode() : Specified mask (%u,%u,%u,%u,%p) is not scalar.",
19656                                     cimg_instance,
19657                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
19658 
19659       if (is_empty()) return *this;
19660       typedef _cimg_Tt Tt;
19661       CImg<Tt> res(_width,_height,_depth,_spectrum);
19662       const int
19663         mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
19664         mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
19665         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
19666       cimg_forC(*this,c)
19667         if (!weighted_erosion) { // Classical erosion
19668           for (int z = mz1; z<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
19669             Tt min_val = cimg::type<Tt>::max();
19670             for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19671               const Tt cval = (Tt)(*this)(x+xm,y+ym,z+zm,c);
19672               if (mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
19673             }
19674             res(x,y,z,c) = min_val;
19675           }
19676           if (cond)
19677             cimg_forYZC(*this,y,z,c)
19678               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19679                 Tt min_val = cimg::type<Tt>::max();
19680                 for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19681                   const T cval = (Tt)_atXYZ(x+xm,y+ym,z+zm,c);
19682                   if (mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
19683                 }
19684                 res(x,y,z,c) = min_val;
19685               }
19686           else
19687             cimg_forYZC(*this,y,z,c)
19688               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19689                 Tt min_val = cimg::type<Tt>::max();
19690                 for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19691                   const T cval = (Tt)atXYZ(x+xm,y+ym,z+zm,c,0);
19692                   if (mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
19693                 }
19694                 res(x,y,z,c) = min_val;
19695               }
19696         } else { // Weighted erosion
19697           for (int z = mz1; z<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
19698             Tt min_val = cimg::type<Tt>::max();
19699             for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19700               const t mval = mask(mx1+xm,my1+ym,mz1+zm);
19701               const Tt cval = (Tt)((*this)(x+xm,y+ym,z+zm,c) + mval);
19702               if (mval && cval<min_val) min_val = cval;
19703             }
19704             res(x,y,z,c) = min_val;
19705           }
19706           if (cond)
19707             cimg_forYZC(*this,y,z,c)
19708               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19709                 Tt min_val = cimg::type<Tt>::max();
19710                 for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19711                   const t mval = mask(mx1+xm,my1+ym,mz1+zm);
19712                   const Tt cval = (Tt)(_atXYZ(x+xm,y+ym,z+zm,c) + mval);
19713                   if (mval && cval<min_val) min_val = cval;
19714                 }
19715                 res(x,y,z,c) = min_val;
19716               }
19717           else
19718             cimg_forYZC(*this,y,z,c)
19719               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19720                 Tt min_val = cimg::type<Tt>::max();
19721                 for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19722                   const t mval = mask(mx1+xm,my1+ym,mz1+zm);
19723                   const Tt cval = (Tt)(atXYZ(x+xm,y+ym,z+zm,c,0) + mval);
19724                   if (mval && cval<min_val) min_val = cval;
19725                 }
19726                 res(x,y,z,c) = min_val;
19727               }
19728         }
19729       return res;
19730     }
19731 
19732     //! Erode the image by a rectangular structuring element of size sx,sy,sz.
19733     CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
19734       if (sx>1 && _width>1) { // Along X-axis.
19735         const int L = width(), off = 1, s = (int)sx, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
19736         CImg<T> buf(L);
19737         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
19738         cimg_forYZC(*this,y,z,c) {
19739           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
19740           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
19741           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
19742           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
19743           for (int p = L - s - 1; p>0; --p) {
19744             const T val = *ptrs; ptrs+=off;
19745             if (is_first) {
19746               const T *nptrs = ptrs - off; cur = val;
19747               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
19748               nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
19749             } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
19750             *(ptrd++) = cur;
19751           }
19752           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
19753           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
19754           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; }
19755           T *pd = data(_width-1,y,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
19756         }
19757       }
19758 
19759       if (sy>1 && _height>1) { // Along Y-axis.
19760         const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
19761         CImg<T> buf(L);
19762         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
19763         cimg_forXZC(*this,x,z,c) {
19764           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
19765           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
19766           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
19767           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
19768           for (int p = L - s - 1; p>0; --p) {
19769             const T val = *ptrs; ptrs+=off;
19770             if (is_first) {
19771               const T *nptrs = ptrs - off; cur = val;
19772               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
19773               nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
19774             } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
19775             *(ptrd++) = cur;
19776           }
19777           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
19778           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
19779           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; }
19780           T *pd = data(x,_height-1,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
19781         }
19782       }
19783 
19784       if (sz>1 && _depth>1) { // Along Z-axis.
19785         const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
19786         CImg<T> buf(L);
19787         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
19788         cimg_forXYC(*this,x,y,c) {
19789           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
19790           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
19791           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
19792           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
19793           for (int p = L - s - 1; p>0; --p) {
19794             const T val = *ptrs; ptrs+=off;
19795             if (is_first) {
19796               const T *nptrs = ptrs - off; cur = val;
19797               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
19798               nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
19799             } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
19800             *(ptrd++) = cur;
19801           }
19802           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
19803           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
19804           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; }
19805           T *pd = data(x,y,_depth-1,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
19806         }
19807       }
19808       return *this;
19809     }
19810 
19811     CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
19812       return (+*this).erode(sx,sy,sz);
19813     }
19814 
19815     //! Erode the image by a square structuring element of size sx.
19816     CImg<T>& erode(const unsigned int s) {
19817       return erode(s,s,s);
19818     }
19819 
19820     CImg<T> get_erode(const unsigned int s) const {
19821       return (+*this).erode(s);
19822     }
19823 
19824     //! Dilate the image by a structuring element.
19825     template<typename t>
19826     CImg<T>& dilate(const CImg<t>& mask, const unsigned int cond=1, const bool weighted_dilatation=false) {
19827       return get_dilate(mask,cond,weighted_dilatation).move_to(*this);
19828     }
19829 
19830     template<typename t>
19831     CImg<_cimg_Tt> get_dilate(const CImg<t>& mask, const unsigned int cond=1,
19832                               const bool weighted_dilatation=false) const {
19833       if (!mask || mask._spectrum!=1)
19834         throw CImgArgumentException(_cimg_instance
19835                                     "dilate() : Specified mask (%u,%u,%u,%u,%p) is not scalar.",
19836                                     cimg_instance,
19837                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
19838 
19839       if (is_empty()) return *this;
19840       typedef _cimg_Tt Tt;
19841       CImg<Tt> res(_width,_height,_depth,_spectrum);
19842       const int
19843         mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
19844         mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
19845         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
19846       cimg_forC(*this,c)
19847         if (!weighted_dilatation) { // Classical dilatation
19848           for (int z = mz1; z<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
19849             Tt max_val = cimg::type<Tt>::min();
19850             for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19851               const Tt cval = (Tt)(*this)(x+xm,y+ym,z+zm,c);
19852               if (mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
19853             }
19854             res(x,y,z,c) = max_val;
19855           }
19856           if (cond)
19857             cimg_forYZC(*this,y,z,c)
19858               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19859                 Tt max_val = cimg::type<Tt>::min();
19860                 for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19861                   const T cval = (Tt)_atXYZ(x+xm,y+ym,z+zm,c);
19862                   if (mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
19863                 }
19864                 res(x,y,z,c) = max_val;
19865               }
19866           else
19867             cimg_forYZC(*this,y,z,c)
19868               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19869                 Tt max_val = cimg::type<Tt>::min();
19870                 for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19871                   const T cval = (Tt)atXYZ(x+xm,y+ym,z+zm,c,0);
19872                   if (mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
19873                 }
19874                 res(x,y,z,c) = max_val;
19875               }
19876         } else { // Weighted dilatation
19877           for (int z = mz1; z<mze; ++z) for (int y = my1; y<mye; ++y) for (int x = mx1; x<mxe; ++x) {
19878             Tt max_val = cimg::type<Tt>::min();
19879             for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19880               const t mval = mask(mx1+xm,my1+ym,mz1+zm);
19881               const Tt cval = (Tt)((*this)(x+xm,y+ym,z+zm,c) - mval);
19882               if (mval && cval>max_val) max_val = cval;
19883             }
19884             res(x,y,z,c) = max_val;
19885           }
19886           if (cond)
19887             cimg_forYZC(*this,y,z,c)
19888               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19889                 Tt max_val = cimg::type<Tt>::min();
19890                 for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19891                   const t mval = mask(mx1+xm,my1+ym,mz1+zm);
19892                   const Tt cval = (Tt)(_atXYZ(x+xm,y+ym,z+zm,c) - mval);
19893                   if (mval && cval>max_val) max_val = cval;
19894                 }
19895                 res(x,y,z,c) = max_val;
19896               }
19897           else
19898             cimg_forYZC(*this,y,z,c)
19899               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
19900                 Tt max_val = cimg::type<Tt>::min();
19901                 for (int zm = -mz1; zm<=mz2; ++zm) for (int ym = -my1; ym<=my2; ++ym) for (int xm = -mx1; xm<=mx2; ++xm) {
19902                   const t mval = mask(mx1+xm,my1+ym,mz1+zm);
19903                   const Tt cval = (Tt)(atXYZ(x+xm,y+ym,z+zm,c,0) - mval);
19904                   if (mval && cval>max_val) max_val = cval;
19905                 }
19906                 res(x,y,z,c) = max_val;
19907               }
19908         }
19909       return res;
19910     }
19911 
19912     //! Dilate the image by a rectangular structuring element of size sx,sy,sz.
19913     CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
19914       if (sx>1 && _width>1) { // Along X-axis.
19915         const int L = width(), off = 1, s = (int)sx, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
19916         CImg<T> buf(L);
19917         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
19918         cimg_forYZC(*this,y,z,c) {
19919           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
19920           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
19921           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
19922           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
19923           for (int p = L - s - 1; p>0; --p) {
19924             const T val = *ptrs; ptrs+=off;
19925             if (is_first) {
19926               const T *nptrs = ptrs - off; cur = val;
19927               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
19928               nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
19929             } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
19930             *(ptrd++) = cur;
19931           }
19932           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
19933           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
19934           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; }
19935           T *pd = data(_width-1,y,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
19936         }
19937       }
19938 
19939       if (sy>1 && _height>1) { // Along Y-axis.
19940         const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
19941         CImg<T> buf(L);
19942         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
19943         cimg_forXZC(*this,x,z,c) {
19944           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
19945           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
19946           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
19947           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
19948           for (int p = L - s - 1; p>0; --p) {
19949             const T val = *ptrs; ptrs+=off;
19950             if (is_first) {
19951               const T *nptrs = ptrs - off; cur = val;
19952               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
19953               nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
19954             } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
19955             *(ptrd++) = cur;
19956           }
19957           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
19958           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
19959           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; }
19960           T *pd = data(x,_height-1,z,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
19961         }
19962       }
19963 
19964       if (sz>1 && _depth>1) { // Along Z-axis.
19965         const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
19966         CImg<T> buf(L);
19967         T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
19968         cimg_forXYC(*this,x,y,c) {
19969           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
19970           ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
19971           for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
19972           for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
19973           for (int p = L - s - 1; p>0; --p) {
19974             const T val = *ptrs; ptrs+=off;
19975             if (is_first) {
19976               const T *nptrs = ptrs - off; cur = val;
19977               for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
19978               nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
19979             } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
19980             *(ptrd++) = cur;
19981           }
19982           ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
19983           for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
19984           for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; }
19985           T *pd = data(x,y,_depth-1,c) + off; cimg_for(buf,ps,T) { pd-=off; *pd = *ps; }
19986         }
19987       }
19988       return *this;
19989     }
19990 
19991     CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
19992       return (+*this).dilate(sx,sy,sz);
19993     }
19994 
19995     //! Erode the image by a square structuring element of size sx.
19996     CImg<T>& dilate(const unsigned int s) {
19997       return dilate(s,s,s);
19998     }
19999 
20000     CImg<T> get_dilate(const unsigned int s) const {
20001       return (+*this).dilate(s);
20002     }
20003 
20004     //! Compute the watershed transform, from an instance image of non-zero labels.
20005     template<typename t>
20006     CImg<T>& watershed(const CImg<t>& priority, const bool fill_lines=true) {
20007       if (is_empty()) return *this;
20008       if (!is_sameXYZ(priority))
20009         throw CImgArgumentException(_cimg_instance
20010                                     "watershed() : Instance image and specified priority (%u,%u,%u,%u,%p) have different dimensions.",
20011                                     cimg_instance,priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
20012       if (_spectrum!=1) { cimg_forC(*this,c) get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum),fill_lines); return *this; }
20013 
20014       CImg<boolT> in_queue(_width,_height,_depth,1,0);
20015       CImg<typename cimg::superset2<T,t,int>::type> Q;
20016       unsigned int sizeQ = 0;
20017 
20018       // Find seed points and insert them in priority queue.
20019       const T *ptrs = _data;
20020       cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) {
20021         if (x-1>=0 && !(*this)(x-1,y,z))       Q._watershed_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
20022         if (x+1<width() && !(*this)(x+1,y,z))  Q._watershed_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
20023         if (y-1>=0 && !(*this)(x,y-1,z))       Q._watershed_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
20024         if (y+1<height() && !(*this)(x,y+1,z)) Q._watershed_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
20025         if (z-1>=0 && !(*this)(x,y,z-1))       Q._watershed_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
20026         if (z+1<depth() && !(*this)(x,y,z+1))  Q._watershed_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
20027       }
20028 
20029       // Start watershed computation.
20030       while (sizeQ) {
20031 
20032         // Get and remove point with minimal priority from the queue.
20033         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
20034         Q._watershed_remove(sizeQ);
20035 
20036         // Check labels of the neighbors.
20037         bool is_same_label = true;
20038         unsigned int label = 0;
20039         if (x-1>=0) {
20040           if ((*this)(x-1,y,z)) { if (!label) label = (*this)(x-1,y,z); else if (label!=(*this)(x-1,y,z)) is_same_label = false; }
20041           else Q._watershed_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
20042         }
20043         if (x+1<width()) {
20044           if ((*this)(x+1,y,z)) { if (!label) label = (*this)(x+1,y,z); else if (label!=(*this)(x+1,y,z)) is_same_label = false; }
20045           else Q._watershed_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
20046         }
20047         if (y-1>=0) {
20048           if ((*this)(x,y-1,z)) { if (!label) label = (*this)(x,y-1,z); else if (label!=(*this)(x,y-1,z)) is_same_label = false; }
20049           else Q._watershed_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
20050         }
20051         if (y+1<height()) {
20052           if ((*this)(x,y+1,z)) { if (!label) label = (*this)(x,y+1,z); else if (label!=(*this)(x,y+1,z)) is_same_label = false; }
20053           else Q._watershed_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
20054         }
20055         if (z-1>=0) {
20056           if ((*this)(x,y,z-1)) { if (!label) label = (*this)(x,y,z-1); else if (label!=(*this)(x,y,z-1)) is_same_label = false; }
20057           else Q._watershed_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
20058         }
20059         if (z+1<depth()) {
20060           if ((*this)(x,y,z+1)) { if (!label) label = (*this)(x,y,z+1); else if (label!=(*this)(x,y,z+1)) is_same_label = false; }
20061           else Q._watershed_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
20062         }
20063         if (is_same_label) (*this)(x,y,z) = label;
20064       }
20065 
20066       // Fill lines.
20067       if (fill_lines) {
20068 
20069         // Sort all non-labeled pixels with labeled neighbors.
20070         in_queue = false;
20071         const T *ptrs = _data;
20072         cimg_forXYZ(*this,x,y,z) if (!*(ptrs++) &&
20073                                      ((x-1>=0 && (*this)(x-1,y,z)) || (x+1<width() && (*this)(x+1,y,z)) ||
20074                                       (y-1>=0 && (*this)(x,y-1,z)) || (y+1<height() && (*this)(x,y+1,z)) ||
20075                                       (z-1>=0 && (*this)(x,y,z-1)) || (z+1>depth() && (*this)(x,y,z+1))))
20076           Q._watershed_insert(in_queue,sizeQ,priority(x,y,z),x,y,z);
20077 
20078         // Start line filling process.
20079         while (sizeQ) {
20080           const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
20081           Q._watershed_remove(sizeQ);
20082           t pmax = cimg::type<t>::min();
20083           int xmax = 0, ymax = 0, zmax = 0;
20084           if (x-1>=0) {
20085             if ((*this)(x-1,y,z)) { if (priority(x-1,y,z)>pmax) { pmax = priority(x-1,y,z); xmax = x-1; ymax = y; zmax = z; }}
20086             else Q._watershed_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
20087           }
20088           if (x+1<width()) {
20089             if ((*this)(x+1,y,z)) { if (priority(x+1,y,z)>pmax) { pmax = priority(x+1,y,z); xmax = x+1; ymax = y; zmax = z; }}
20090             else Q._watershed_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
20091           }
20092           if (y-1>=0) {
20093             if ((*this)(x,y-1,z)) { if (priority(x,y-1,z)>pmax) { pmax = priority(x,y-1,z); xmax = x; ymax = y-1; zmax = z; }}
20094             else Q._watershed_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
20095           }
20096           if (y+1<height()) {
20097             if ((*this)(x,y+1,z)) { if (priority(x,y+1,z)>pmax) { pmax = priority(x,y+1,z); xmax = x; ymax = y+1; zmax = z; }}
20098             else Q._watershed_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
20099           }
20100           if (z-1>=0) {
20101             if ((*this)(x,y,z-1)) { if (priority(x,y,z-1)>pmax) { pmax = priority(x,y,z-1); xmax = x; ymax = y; zmax = z-1; }}
20102             else Q._watershed_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
20103           }
20104           if (z+1<depth()) {
20105             if ((*this)(x,y,z+1)) { if (priority(x,y,z+1)>pmax) { pmax = priority(x,y,z+1); xmax = x; ymax = y; zmax = z+1; }}
20106             else Q._watershed_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
20107           }
20108           (*this)(x,y,z) = (*this)(xmax,ymax,zmax);
20109         }
20110       }
20111       return *this;
20112     }
20113 
20114     template<typename t>
20115     CImg<T> get_watershed(const CImg<t>& priority, const bool fill_lines=true) const {
20116       return (+*this).watershed(priority,fill_lines);
20117     }
20118 
20119     // Insert/Remove items in priority queue, for watershed transform.
20120     template<typename t>
20121     CImg<T>& _watershed_insert(CImg<boolT>& in_queue, unsigned int& siz, const t value, const unsigned int x, const unsigned int y, const unsigned int z) {
20122       if (in_queue(x,y,z)) return *this;
20123       in_queue(x,y,z) = true;
20124       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
20125       (*this)(siz-1,0) = (T)value; (*this)(siz-1,1) = (T)x; (*this)(siz-1,2) = (T)y; (*this)(siz-1,3) = (T)z;
20126       for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos+1)/2-1,0); pos = par) {
20127         cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
20128         cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
20129       }
20130       return *this;
20131     }
20132 
20133     CImg<T>& _watershed_remove(unsigned int& siz) {
20134       (*this)(0,0) = (*this)(--siz,0); (*this)(0,1) = (*this)(siz,1); (*this)(0,2) = (*this)(siz,2); (*this)(0,3) = (*this)(siz,3);
20135       const float value = (*this)(0,0);
20136       for (unsigned int pos = 0, left = 0, right = 0;
20137            ((right=2*(pos+1),(left=right-1))<siz && value<(*this)(left,0)) || (right<siz && value<(*this)(right,0));) {
20138         if (right<siz) {
20139           if ((*this)(left,0)>(*this)(right,0)) {
20140             cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
20141             cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
20142             pos = left;
20143           } else {
20144             cimg::swap((*this)(pos,0),(*this)(right,0)); cimg::swap((*this)(pos,1),(*this)(right,1));
20145             cimg::swap((*this)(pos,2),(*this)(right,2)); cimg::swap((*this)(pos,3),(*this)(right,3));
20146             pos = right;
20147           }
20148         } else {
20149           cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
20150           cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
20151           pos = left;
20152         }
20153       }
20154       return *this;
20155     }
20156 
20157     //! Compute the result of the Deriche filter.
20158     /**
20159        The Canny-Deriche filter is a recursive algorithm allowing to compute blurred derivatives of
20160        order 0,1 or 2 of an image.
20161     **/
20162     CImg<T>& deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) {
20163 #define _cimg_deriche2_apply \
20164   Tfloat *ptrY = Y._data, yb = 0, yp = 0; \
20165   T xp = (T)0; \
20166   if (cond) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \
20167   for (int m = 0; m<N; ++m) { \
20168     const T xc = *ptrX; ptrX+=off; \
20169     const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \
20170     xp = xc; yb = yp; yp = yc; \
20171   } \
20172   T xn = (T)0, xa = (T)0; \
20173   Tfloat yn = 0, ya = 0; \
20174   if (cond) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \
20175   for (int n = N-1; n>=0; --n) { \
20176     const T xc = *(ptrX-=off); \
20177     const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \
20178     xa = xn; xn = xc; ya = yn; yn = yc; \
20179     *ptrX = (T)(*(--ptrY)+yc); \
20180   }
20181       const char naxis = cimg::uncase(axis);
20182       const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
20183       if (is_empty() || (nsigma<0.1 && !order)) return *this;
20184       const float
20185         nnsigma = nsigma<0.1f?0.1f:nsigma,
20186         alpha = 1.695f/nnsigma,
20187         ema = (float)std::exp(-alpha),
20188         ema2 = (float)std::exp(-2*alpha),
20189         b1 = -2*ema,
20190         b2 = ema2;
20191       float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
20192       switch (order) {
20193       case 0 : {
20194         const float k = (1-ema)*(1-ema)/(1+2*alpha*ema-ema2);
20195         a0 = k;
20196         a1 = k*(alpha-1)*ema;
20197         a2 = k*(alpha+1)*ema;
20198         a3 = -k*ema2;
20199       } break;
20200       case 1 : {
20201         const float k = (1-ema)*(1-ema)/ema;
20202         a0 = k*ema;
20203         a1 = a3 = 0;
20204         a2 = -a0;
20205       } break;
20206       case 2 : {
20207         const float
20208           ea = (float)std::exp(-alpha),
20209           k = -(ema2-1)/(2*alpha*ema),
20210           kn = (-2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea));
20211         a0 = kn;
20212         a1 = -kn*(1+k*alpha)*ema;
20213         a2 = kn*(1-k*alpha)*ema;
20214         a3 = -kn*ema2;
20215       } break;
20216       default :
20217         throw CImgArgumentException(_cimg_instance
20218                                     "deriche() : Invalid specified filter order %u "
20219                                     "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
20220                                     cimg_instance,
20221                                     order);
20222       }
20223       coefp = (a0+a1)/(1+b1+b2);
20224       coefn = (a2+a3)/(1+b1+b2);
20225       switch (naxis) {
20226       case 'x' : {
20227         const int N = _width, off = 1;
20228         CImg<Tfloat> Y(N);
20229         cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche2_apply; }
20230       } break;
20231       case 'y' : {
20232         const int N = _height, off = _width;
20233         CImg<Tfloat> Y(N);
20234         cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche2_apply; }
20235       } break;
20236       case 'z' : {
20237         const int N = _depth, off = _width*_height;
20238         CImg<Tfloat> Y(N);
20239         cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche2_apply; }
20240       } break;
20241       default : {
20242         const int N = _spectrum, off = _width*_height*_depth;
20243         CImg<Tfloat> Y(N);
20244         cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche2_apply; }
20245       }
20246       }
20247       return *this;
20248     }
20249 
20250     CImg<Tfloat> get_deriche(const float sigma, const int order=0, const char axis='x', const bool cond=true) const {
20251       return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,cond);
20252     }
20253 
20254     //! Return a blurred version of the image, using a Canny-Deriche filter.
20255     /**
20256        Blur the image with an anisotropic exponential filter (Deriche filter of order 0).
20257     **/
20258     CImg<T>& blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) {
20259       if (!is_empty()) {
20260         if (_width>1) deriche(sigmax,0,'x',cond);
20261         if (_height>1) deriche(sigmay,0,'y',cond);
20262         if (_depth>1) deriche(sigmaz,0,'z',cond);
20263       }
20264       return *this;
20265     }
20266 
20267     CImg<Tfloat> get_blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) const {
20268       return CImg<Tfloat>(*this,false).blur(sigmax,sigmay,sigmaz,cond);
20269     }
20270 
20271     //! Return a blurred version of the image, using a Canny-Deriche filter.
20272     CImg<T>& blur(const float sigma, const bool cond=true) {
20273       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
20274       return blur(nsigma,nsigma,nsigma,cond);
20275     }
20276 
20277     CImg<Tfloat> get_blur(const float sigma, const bool cond=true) const {
20278       return CImg<Tfloat>(*this,false).blur(sigma,cond);
20279     }
20280 
20281     //! Blur the image anisotropically following a field of diffusion tensors.
20282     /**
20283        \param G = Field of square roots of diffusion tensors/vectors used to drive the smoothing.
20284        \param amplitude = amplitude of the smoothing.
20285        \param dl = spatial discretization.
20286        \param da = angular discretization.
20287        \param gauss_prec = precision of the gaussian function.
20288        \param interpolation Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta)
20289        \param fast_approx = Tell to use the fast approximation or not.
20290     **/
20291     template<typename t>
20292     CImg<T>& blur_anisotropic(const CImg<t>& G,
20293                               const float amplitude=60, const float dl=0.8f, const float da=30,
20294                               const float gauss_prec=2, const unsigned int interpolation_type=0,
20295                               const bool fast_approx=1) {
20296 
20297       // Check arguments and init variables
20298       if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
20299         throw CImgArgumentException(_cimg_instance
20300                                     "blur_anisotropic() : Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
20301                                     cimg_instance,
20302                                     G._width,G._height,G._depth,G._spectrum,G._data);
20303 
20304       if (is_empty() || amplitude<=0 || dl<0) return *this;
20305       const bool is_threed = (G._spectrum==6);
20306       T val_min, val_max = max_min(val_min);
20307 
20308       if (da<=0) {  // Iterated oriented Laplacians
20309         CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
20310         for (unsigned int iteration = 0; iteration<(unsigned int)amplitude; ++iteration) {
20311           Tfloat *ptrd = velocity._data, veloc_max = 0;
20312           if (is_threed) { // 3d version
20313             CImg_3x3x3(I,Tfloat);
20314             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
20315               const Tfloat
20316                 ixx = Incc + Ipcc - 2*Iccc,
20317                 ixy = (Innc + Ippc - Inpc - Ipnc)/4,
20318                 ixz = (Incn + Ipcp - Incp - Ipcn)/4,
20319                 iyy = Icnc + Icpc - 2*Iccc,
20320                 iyz = (Icnn + Icpp - Icnp - Icpn)/4,
20321                 izz = Iccn + Iccp - 2*Iccc,
20322                 veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz + G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
20323               *(ptrd++) = veloc;
20324               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
20325             }
20326           } else { // 2d version
20327             CImg_3x3(I,Tfloat);
20328             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
20329               const Tfloat
20330                 ixx = Inc + Ipc - 2*Icc,
20331                 ixy = (Inn + Ipp - Inp - Ipn)/4,
20332                 iyy = Icn + Icp - 2*Icc,
20333                 veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
20334               *(ptrd++) = veloc;
20335               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
20336             }
20337           }
20338           if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
20339         }
20340       } else { // LIC-based smoothing.
20341         const unsigned int whd = _width*_height*_depth;
20342         const float sqrt2amplitude = (float)std::sqrt(2*amplitude);
20343         const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
20344         CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_threed?4:3), val(_spectrum);
20345         int N = 0;
20346         if (is_threed) { // 3d version
20347           for (float phi = (180%(int)da)/2.0f; phi<=180; phi+=da) {
20348             const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), da2 = datmp<1?360.0f:datmp;
20349             for (float theta = 0; theta<360; (theta+=da2),++N) {
20350               const float
20351                 thetar = (float)(theta*cimg::PI/180),
20352                 vx = (float)(std::cos(thetar)*std::cos(phir)),
20353                 vy = (float)(std::sin(thetar)*std::cos(phir)),
20354                 vz = (float)std::sin(phir);
20355               const t
20356                 *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
20357                 *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
20358               Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2), *pd3 = W.data(0,0,0,3);
20359               cimg_forXYZ(G,xg,yg,zg) {
20360                 const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
20361                 const float
20362                   u = (float)(a*vx + b*vy + c*vz),
20363                   v = (float)(b*vx + d*vy + e*vz),
20364                   w = (float)(c*vx + e*vy + f*vz),
20365                   n = (float)std::sqrt(1e-5+u*u+v*v+w*w),
20366                   dln = dl/n;
20367                 *(pd0++) = (Tfloat)(u*dln);
20368                 *(pd1++) = (Tfloat)(v*dln);
20369                 *(pd2++) = (Tfloat)(w*dln);
20370                 *(pd3++) = (Tfloat)n;
20371               }
20372 
20373               Tfloat *ptrd = res._data;
20374               cimg_forXYZ(*this,x,y,z) {
20375                 val.fill(0);
20376                 const float
20377                   n = (float)W(x,y,z,3),
20378                   fsigma = (float)(n*sqrt2amplitude),
20379                   fsigma2 = 2*fsigma*fsigma,
20380                   length = gauss_prec*fsigma;
20381                 float
20382                   S = 0,
20383                   X = (float)x,
20384                   Y = (float)y,
20385                   Z = (float)z;
20386                 switch (interpolation_type) {
20387                 case 0 : { // Nearest neighbor
20388                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
20389                     const int
20390                       cx = (int)(X+0.5f),
20391                       cy = (int)(Y+0.5f),
20392                       cz = (int)(Z+0.5f);
20393                     const float
20394                       u = (float)W(cx,cy,cz,0),
20395                       v = (float)W(cx,cy,cz,1),
20396                       w = (float)W(cx,cy,cz,2);
20397                     if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
20398                     else {
20399                       const float coef = (float)std::exp(-l*l/fsigma2);
20400                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
20401                       S+=coef;
20402                     }
20403                     X+=u; Y+=v; Z+=w;
20404                   }
20405                 } break;
20406                 case 1 : { // Linear interpolation
20407                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
20408                     const float
20409                       u = (float)(W._linear_atXYZ(X,Y,Z,0)),
20410                       v = (float)(W._linear_atXYZ(X,Y,Z,1)),
20411                       w = (float)(W._linear_atXYZ(X,Y,Z,2));
20412                     if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
20413                     else {
20414                       const float coef = (float)std::exp(-l*l/fsigma2);
20415                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
20416                       S+=coef;
20417                     }
20418                     X+=u; Y+=v; Z+=w;
20419                   }
20420                 } break;
20421                 default : { // 2nd order Runge Kutta
20422                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
20423                     const float
20424                       u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
20425                       v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
20426                       w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
20427                       u = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,0)),
20428                       v = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,1)),
20429                       w = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,2));
20430                     if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
20431                     else {
20432                       const float coef = (float)std::exp(-l*l/fsigma2);
20433                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
20434                       S+=coef;
20435                     }
20436                     X+=u; Y+=v; Z+=w;
20437                   }
20438                 } break;
20439                 }
20440                 Tfloat *_ptrd = ptrd++;
20441                 if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; }
20442                 else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,z,c)); _ptrd+=whd; }
20443               }
20444             }
20445           }
20446         } else { // 2d LIC algorithm
20447           for (float theta = (360%(int)da)/2.0f; theta<360; (theta+=da),++N) {
20448             const float thetar = (float)(theta*cimg::PI/180), vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
20449             const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
20450             Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
20451             cimg_forXY(G,xg,yg) {
20452               const t a = *(pa++), b = *(pb++), c = *(pc++);
20453               const float
20454                 u = (float)(a*vx + b*vy),
20455                 v = (float)(b*vx + c*vy),
20456                 n = (float)std::sqrt(1e-5+u*u+v*v),
20457                 dln = dl/n;
20458               *(pd0++) = (Tfloat)(u*dln);
20459               *(pd1++) = (Tfloat)(v*dln);
20460               *(pd2++) = (Tfloat)n;
20461             }
20462             Tfloat *ptrd = res._data;
20463             cimg_forXY(*this,x,y) {
20464               val.fill(0);
20465               const float
20466                 n = (float)W(x,y,0,2),
20467                 fsigma = (float)(n*sqrt2amplitude),
20468                 fsigma2 = 2*fsigma*fsigma,
20469                 length = gauss_prec*fsigma;
20470               float
20471                 S = 0,
20472                 X = (float)x,
20473                 Y = (float)y;
20474               switch (interpolation_type) {
20475               case 0 : { // Nearest-neighbor
20476                 for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
20477                   const int
20478                     cx = (int)(X+0.5f),
20479                     cy = (int)(Y+0.5f);
20480                   const float
20481                     u = (float)W(cx,cy,0,0),
20482                     v = (float)W(cx,cy,0,1);
20483                   if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
20484                   else {
20485                     const float coef = (float)std::exp(-l*l/fsigma2);
20486                     cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
20487                     S+=coef;
20488                   }
20489                   X+=u; Y+=v;
20490                 }
20491               } break;
20492               case 1 : { // Linear interpolation
20493                 for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
20494                   const float
20495                     u = (float)(W._linear_atXY(X,Y,0,0)),
20496                     v = (float)(W._linear_atXY(X,Y,0,1));
20497                   if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
20498                   else {
20499                     const float coef = (float)std::exp(-l*l/fsigma2);
20500                     cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
20501                     S+=coef;
20502                   }
20503                   X+=u; Y+=v;
20504                 }
20505               } break;
20506               default : { // 2nd-order Runge-kutta interpolation
20507                 for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
20508                   const float
20509                     u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
20510                     v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
20511                     u = (float)(W._linear_atXY(X+u0,Y+v0,0,0)),
20512                     v = (float)(W._linear_atXY(X+u0,Y+v0,0,1));
20513                   if (fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
20514                   else {
20515                     const float coef = (float)std::exp(-l*l/fsigma2);
20516                     cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
20517                     S+=coef;
20518                   }
20519                   X+=u; Y+=v;
20520                 }
20521               }
20522               }
20523               Tfloat *_ptrd = ptrd++;
20524               if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; }
20525               else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,0,c)); _ptrd+=whd; }
20526             }
20527           }
20528         }
20529         const Tfloat *ptrs = res.end();
20530         cimg_for(*this,ptrd,T) { const Tfloat val = *(--ptrs)/N; *ptrd = val<val_min?val_min:(val>val_max?val_max:(T)val); }
20531       }
20532       return *this;
20533     }
20534 
20535     template<typename t>
20536     CImg<T> get_blur_anisotropic(const CImg<t>& G,
20537                                  const float amplitude=60, const float dl=0.8f, const float da=30,
20538                                  const float gauss_prec=2, const unsigned int interpolation_type=0,
20539                                  const bool fast_approx=true) const {
20540       return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,fast_approx);
20541     }
20542 
20543     //! Blur an image following in an anisotropic way.
20544     CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
20545                               const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
20546                               const float gauss_prec=2, const unsigned int interpolation_type=0,
20547                               const bool fast_approx=true) {
20548       return blur_anisotropic(get_edge_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3),
20549                               amplitude,dl,da,gauss_prec,interpolation_type,fast_approx);
20550     }
20551 
20552     CImg<T> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
20553                                  const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
20554                                  const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0,
20555                                  const bool fast_approx=true) const {
20556       return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,fast_approx);
20557     }
20558 
20559     //! Blur an image using the bilateral filter.
20560     /**
20561        \param sigma_x Amount of blur along the X-axis.
20562        \param sigma_y Amount of blur along the Y-axis.
20563        \param sigma_z Amount of blur along the Z-axis.
20564        \param sigma_r Amount of blur along the range axis.
20565        \param bgrid_x Size of the bilateral grid along the X-axis.
20566        \param bgrid_y Size of the bilateral grid along the Y-axis.
20567        \param bgrid_z Size of the bilateral grid along the Z-axis.
20568        \param bgrid_r Size of the bilateral grid along the range axis.
20569        \param interpolation_type Use interpolation for image slicing.
20570        \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
20571        (extended for 3d volumetric images).
20572     **/
20573     CImg<T>& blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
20574                             const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
20575                             const bool interpolation_type=true) {
20576       if (is_empty()) return *this;
20577       T m, M = max_min(m);
20578       const float range = (float)(1.0f+M-m);
20579       const unsigned int
20580         bx0 = bgrid_x>=0?bgrid_x:_width*(-bgrid_x)/100,
20581         by0 = bgrid_y>=0?bgrid_y:_height*(-bgrid_y)/100,
20582         bz0 = bgrid_z>=0?bgrid_z:_depth*(-bgrid_z)/100,
20583         br0 = bgrid_r>=0?bgrid_r:(int)(-range*bgrid_r/100),
20584         bx = bx0>0?bx0:1,
20585         by = by0>0?by0:1,
20586         bz = bz0>0?bz0:1,
20587         br = br0>0?br0:1;
20588       const float
20589         _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
20590         _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
20591         _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
20592         nsigma_x = _sigma_x*bx/_width,
20593         nsigma_y = _sigma_y*by/_height,
20594         nsigma_z = _sigma_z*bz/_depth,
20595         nsigma_r = sigma_r*br/range;
20596       if (nsigma_x>0 || nsigma_y>0 || nsigma_z>0 || nsigma_r>0) {
20597         const bool is_threed = (_depth>1);
20598         if (is_threed) { // 3d version of the algorithm
20599           CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
20600           cimg_forC(*this,c) {
20601             bgrid.fill(0); bgridw.fill(0);
20602             cimg_forXYZ(*this,x,y,z) {
20603               const T val = (*this)(x,y,z,c);
20604               const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range);
20605               bgrid(X,Y,Z,R) = (float)val;
20606               bgridw(X,Y,Z,R) = 1;
20607             }
20608             bgrid.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
20609             bgridw.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
20610             if (interpolation_type) cimg_forXYZ(*this,x,y,z) {
20611               const T val = (*this)(x,y,z,c);
20612               const float X = (float)x*bx/_width, Y = (float)y*by/_height, Z = (float)z*bz/_depth, R = (float)((val-m)*br/range),
20613                 bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
20614               (*this)(x,y,z,c) = (T)(bval0/bval1);
20615             } else cimg_forXYZ(*this,x,y,z) {
20616               const T val = (*this)(x,y,z,c);
20617               const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range);
20618               const float bval0 = bgrid(X,Y,Z,R), bval1 = bgridw(X,Y,Z,R);
20619               (*this)(x,y,z,c) = (T)(bval0/bval1);
20620             }
20621           }
20622         } else { // 2d version of the algorithm
20623           CImg<floatT> bgrid(bx,by,br,2);
20624           cimg_forC(*this,c) {
20625             bgrid.fill(0);
20626             cimg_forXY(*this,x,y) {
20627               const T val = (*this)(x,y,c);
20628               const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range);
20629               bgrid(X,Y,R,0) = (float)val;
20630               bgrid(X,Y,R,1) = 1;
20631             }
20632             bgrid.blur(nsigma_x,nsigma_y,0,true).blur(0,0,nsigma_r,false);
20633             if (interpolation_type) cimg_forXY(*this,x,y) {
20634               const T val = (*this)(x,y,c);
20635               const float X = (float)x*bx/_width, Y = (float)y*by/_height, R = (float)((val-m)*br/range),
20636                 bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
20637               (*this)(x,y,c) = (T)(bval0/bval1);
20638             } else cimg_forXY(*this,x,y) {
20639               const T val = (*this)(x,y,c);
20640               const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range);
20641               const float bval0 = bgrid(X,Y,R,0), bval1 = bgrid(X,Y,R,1);
20642               (*this)(x,y,c) = (T)(bval0/bval1);
20643             }
20644           }
20645         }
20646       }
20647       return *this;
20648     }
20649 
20650     CImg<T> get_blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
20651                                const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
20652                                const bool interpolation_type=true) const {
20653       return (+*this).blur_bilateral(sigma_x,sigma_y,sigma_z,sigma_r,bgrid_x,bgrid_y,bgrid_z,bgrid_r,interpolation_type);
20654     }
20655 
20656     //! Blur an image using the bilateral filter.
20657     CImg<T>& blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
20658                             const bool interpolation_type=true) {
20659       const float nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
20660       return blur_bilateral(nsigma_s,nsigma_s,nsigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type);
20661     }
20662 
20663     CImg<T> get_blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
20664                                const bool interpolation_type=true) const {
20665       return (+*this).blur_bilateral(sigma_s,sigma_s,sigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type);
20666     }
20667 
20668     //! Blur an image in its patch-based space.
20669     CImg<T>& blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
20670                         const unsigned int lookup_size=4, const float smoothness=0, const bool fast_approx=true) {
20671       return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,fast_approx).move_to(*this);
20672     }
20673 
20674     CImg<T> get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
20675                            const unsigned int lookup_size=4, const float smoothness=0, const bool fast_approx=true) const {
20676 
20677 #define _cimg_blur_patch3d_fast(N) \
20678       cimg_for##N##XYZ(res,x,y,z) { \
20679         T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
20680         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
20681         float sum_weights = 0; \
20682         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0) - img(p,q,r,0))<sigma_p3) { \
20683           T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
20684           float distance2 = 0; \
20685           pQ = Q.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \
20686           distance2/=Pnorm; \
20687           const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
20688             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
20689           sum_weights+=weight; \
20690           cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
20691         } \
20692         if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
20693         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
20694     }
20695 
20696 #define _cimg_blur_patch3d(N) \
20697       cimg_for##N##XYZ(res,x,y,z) { \
20698         T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
20699         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
20700         float sum_weights = 0, weight_max = 0; \
20701         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
20702           T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
20703           float distance2 = 0; \
20704           pQ = Q.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \
20705           distance2/=Pnorm; \
20706           const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
20707             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)std::exp(-alldist); \
20708           if (weight>weight_max) weight_max = weight; \
20709           sum_weights+=weight; \
20710           cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
20711         } \
20712         sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); \
20713         if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
20714         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
20715       }
20716 
20717 #define _cimg_blur_patch2d_fast(N) \
20718         cimg_for##N##XY(res,x,y) { \
20719           T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
20720           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
20721           float sum_weights = 0; \
20722           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0,0) - img(p,q,0,0))<sigma_p3) { \
20723             T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
20724             float distance2 = 0; \
20725             pQ = Q.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \
20726             distance2/=Pnorm; \
20727             const float dx = (float)p - x, dy = (float)q - y, \
20728               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
20729             sum_weights+=weight; \
20730             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
20731           } \
20732           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
20733           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
20734         }
20735 
20736 #define _cimg_blur_patch2d(N) \
20737         cimg_for##N##XY(res,x,y) { \
20738           T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
20739           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
20740           float sum_weights = 0, weight_max = 0; \
20741           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
20742             T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
20743             float distance2 = 0; \
20744             pQ = Q.end(); cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(--pQ); distance2+=dI*dI; } \
20745             distance2/=Pnorm; \
20746             const float dx = (float)p - x, dy = (float)q - y, \
20747               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)std::exp(-alldist); \
20748             if (weight>weight_max) weight_max = weight; \
20749             sum_weights+=weight; \
20750             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
20751           } \
20752           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); \
20753           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
20754           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
20755     }
20756 
20757       CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
20758       const CImg<T> _img = smoothness>0?get_blur(smoothness):CImg<Tfloat>(),&img = smoothness>0?_img:*this;
20759       CImg<T> P(patch_size*patch_size*_spectrum), Q(P);
20760       const float
20761         nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
20762         sigma_s2 = nsigma_s*nsigma_s, sigma_p2 = sigma_p*sigma_p, sigma_p3 = 3*sigma_p,
20763         Pnorm = P.size()*sigma_p2;
20764       const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
20765       const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
20766       if (_depth>1) switch (patch_size) { // 3d
20767       case 2 : if (fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
20768       case 3 : if (fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
20769       default : {
20770         const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
20771         if (fast_approx) cimg_forXYZ(res,x,y,z) { // Fast
20772           P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
20773           const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
20774           float sum_weights = 0;
20775           cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0)-img(p,q,r,0))<sigma_p3) {
20776             (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
20777             const float
20778               dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
20779               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
20780               weight = distance2>3?0.0f:1.0f;
20781             sum_weights+=weight;
20782             cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
20783           }
20784           if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
20785           else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
20786         } else cimg_forXYZ(res,x,y,z) { // Exact
20787           P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
20788           const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
20789           float sum_weights = 0, weight_max = 0;
20790           cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
20791             (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
20792             const float
20793               dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
20794               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
20795               weight = (float)std::exp(-distance2);
20796             if (weight>weight_max) weight_max = weight;
20797             sum_weights+=weight;
20798             cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
20799           }
20800           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c);
20801           if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
20802           else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
20803         }
20804       }
20805       } else switch (patch_size) { // 2d
20806       case 2 : if (fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
20807       case 3 : if (fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
20808       case 4 : if (fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
20809       case 5 : if (fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
20810       case 6 : if (fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
20811       case 7 : if (fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
20812       case 8 : if (fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
20813       case 9 : if (fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
20814       default : { // Fast
20815         const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
20816         if (fast_approx) cimg_forXY(res,x,y) { // 2d fast approximation.
20817           P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
20818           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
20819           float sum_weights = 0;
20820           cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0)-img(p,q,0))<sigma_p3) {
20821             (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
20822             const float
20823               dx = (float)x - p, dy = (float)y - q,
20824               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
20825               weight = distance2>3?0.0f:1.0f;
20826             sum_weights+=weight;
20827             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
20828           }
20829           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
20830           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
20831         } else cimg_forXY(res,x,y) { // 2d exact algorithm.
20832           P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
20833           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
20834           float sum_weights = 0, weight_max = 0;
20835           cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
20836             (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
20837             const float
20838               dx = (float)x - p, dy = (float)y - q,
20839               distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
20840               weight = (float)std::exp(-distance2);
20841             if (weight>weight_max) weight_max = weight;
20842             sum_weights+=weight;
20843             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
20844           }
20845           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c);
20846           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
20847           else cimg_forC(res,c) res(x,y,0,c) = (Tfloat)((*this)(x,y,c));
20848         }
20849       }
20850       }
20851       return res;
20852     }
20853 
20854     //! Apply a median filter.
20855     CImg<T>& blur_median(const unsigned int n) {
20856       if (!n) return *this;
20857       return get_blur_median(n).move_to(*this);
20858     }
20859 
20860     CImg<T> get_blur_median(const unsigned int n) const {
20861       if (is_empty() || n<=1) return *this;
20862       CImg<T> res(_width,_height,_depth,_spectrum);
20863       T *ptrd = res._data;
20864       const int hl = n/2, hr = hl - 1 + n%2;
20865       if (res._depth!=1) cimg_forXYZC(*this,x,y,z,c) { // 3d
20866         const int
20867           x0 = x - hl, y0 = y - hl, z0 = z-hl, x1 = x + hr, y1 = y + hr, z1 = z+hr,
20868           nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
20869           nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1, nz1 = z1>=depth()?depth()-1:z1;
20870         *(ptrd++) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
20871       } else {
20872 #define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b)
20873         if (res._height!=1) switch (n) { // 2d
20874         case 3 : {
20875           T I[9] = { 0 };
20876           CImg_3x3(J,T);
20877           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
20878             std::memcpy(J,I,9*sizeof(T));
20879             _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
20880             _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jpn, Jcn);
20881             _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
20882             _cimg_median_sort(Jpp, Jpc); _cimg_median_sort(Jnc, Jnn); _cimg_median_sort(Jcc, Jcn);
20883             _cimg_median_sort(Jpc, Jpn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jnp, Jnc);
20884             _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcc, Jnp); _cimg_median_sort(Jpn, Jcc);
20885             _cimg_median_sort(Jcc, Jnp);
20886             *(ptrd++) = Jcc;
20887           }
20888         } break;
20889         case 5 : {
20890           T I[25] = { 0 };
20891           CImg_5x5(J,T);
20892           cimg_forC(*this,c) cimg_for5x5(*this,x,y,0,c,I,T) {
20893             std::memcpy(J,I,25*sizeof(T));
20894             _cimg_median_sort(Jbb, Jpb); _cimg_median_sort(Jnb, Jab); _cimg_median_sort(Jcb, Jab); _cimg_median_sort(Jcb, Jnb);
20895             _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbp, Jcp); _cimg_median_sort(Jbp, Jpp); _cimg_median_sort(Jap, Jbc);
20896             _cimg_median_sort(Jnp, Jbc); _cimg_median_sort(Jnp, Jap); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jpc, Jnc);
20897             _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jbn, Jpn); _cimg_median_sort(Jac, Jpn); _cimg_median_sort(Jac, Jbn);
20898             _cimg_median_sort(Jnn, Jan); _cimg_median_sort(Jcn, Jan); _cimg_median_sort(Jcn, Jnn); _cimg_median_sort(Jpa, Jca);
20899             _cimg_median_sort(Jba, Jca); _cimg_median_sort(Jba, Jpa); _cimg_median_sort(Jna, Jaa); _cimg_median_sort(Jcb, Jbp);
20900             _cimg_median_sort(Jnb, Jpp); _cimg_median_sort(Jbb, Jpp); _cimg_median_sort(Jbb, Jnb); _cimg_median_sort(Jab, Jcp);
20901             _cimg_median_sort(Jpb, Jcp); _cimg_median_sort(Jpb, Jab); _cimg_median_sort(Jpc, Jac); _cimg_median_sort(Jnp, Jac);
20902             _cimg_median_sort(Jnp, Jpc); _cimg_median_sort(Jcc, Jbn); _cimg_median_sort(Jap, Jbn); _cimg_median_sort(Jap, Jcc);
20903             _cimg_median_sort(Jnc, Jpn); _cimg_median_sort(Jbc, Jpn); _cimg_median_sort(Jbc, Jnc); _cimg_median_sort(Jba, Jna);
20904             _cimg_median_sort(Jcn, Jna); _cimg_median_sort(Jcn, Jba); _cimg_median_sort(Jpa, Jaa); _cimg_median_sort(Jnn, Jaa);
20905             _cimg_median_sort(Jnn, Jpa); _cimg_median_sort(Jan, Jca); _cimg_median_sort(Jnp, Jcn); _cimg_median_sort(Jap, Jnn);
20906             _cimg_median_sort(Jbb, Jnn); _cimg_median_sort(Jbb, Jap); _cimg_median_sort(Jbc, Jan); _cimg_median_sort(Jpb, Jan);
20907             _cimg_median_sort(Jpb, Jbc); _cimg_median_sort(Jpc, Jba); _cimg_median_sort(Jcb, Jba); _cimg_median_sort(Jcb, Jpc);
20908             _cimg_median_sort(Jcc, Jpa); _cimg_median_sort(Jnb, Jpa); _cimg_median_sort(Jnb, Jcc); _cimg_median_sort(Jnc, Jca);
20909             _cimg_median_sort(Jab, Jca); _cimg_median_sort(Jab, Jnc); _cimg_median_sort(Jac, Jna); _cimg_median_sort(Jbp, Jna);
20910             _cimg_median_sort(Jbp, Jac); _cimg_median_sort(Jbn, Jaa); _cimg_median_sort(Jpp, Jaa); _cimg_median_sort(Jpp, Jbn);
20911             _cimg_median_sort(Jcp, Jpn); _cimg_median_sort(Jcp, Jan); _cimg_median_sort(Jnc, Jpa); _cimg_median_sort(Jbn, Jna);
20912             _cimg_median_sort(Jcp, Jnc); _cimg_median_sort(Jcp, Jbn); _cimg_median_sort(Jpb, Jap); _cimg_median_sort(Jnb, Jpc);
20913             _cimg_median_sort(Jbp, Jcn); _cimg_median_sort(Jpc, Jcn); _cimg_median_sort(Jap, Jcn); _cimg_median_sort(Jab, Jbc);
20914             _cimg_median_sort(Jpp, Jcc); _cimg_median_sort(Jcp, Jac); _cimg_median_sort(Jab, Jpp); _cimg_median_sort(Jab, Jcp);
20915             _cimg_median_sort(Jcc, Jac); _cimg_median_sort(Jbc, Jac); _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbc, Jcc);
20916             _cimg_median_sort(Jpp, Jbc); _cimg_median_sort(Jpp, Jcn); _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcp, Jcn);
20917             _cimg_median_sort(Jcp, Jbc); _cimg_median_sort(Jcc, Jnn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jbc, Jnn);
20918             _cimg_median_sort(Jcc, Jba); _cimg_median_sort(Jbc, Jba); _cimg_median_sort(Jbc, Jcc);
20919             *(ptrd++) = Jcc;
20920           }
20921         } break;
20922         default : {
20923           cimg_forXYC(*this,x,y,c) {
20924             const int
20925               x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
20926               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
20927               nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1;
20928             *(ptrd++) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
20929           }
20930         }
20931         } else switch (n) { // 1d
20932         case 2 : {
20933           T I[4] = { 0 };
20934           cimg_forC(*this,c) cimg_for2x2(*this,x,y,0,c,I,T) *(ptrd++) = (T)(0.5f*(I[0]+I[1]));
20935         } break;
20936         case 3 : {
20937           T I[9] = { 0 };
20938           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T)
20939             *(ptrd++) = I[3]<I[4]?(I[4]<I[5]?I[4]:(I[3]<I[5]?I[5]:I[3])):(I[3]<I[5]?I[3]:(I[4]<I[5]?I[5]:I[4]));
20940         } break;
20941         default : {
20942           cimg_forXC(*this,x,c) {
20943             const int
20944               x0 = x - hl, x1 = x + hr,
20945               nx0 = x0<0?0:x0, nx1 = x1>=width()?width()-1:x1;
20946             *(ptrd++) = get_crop(nx0,0,0,c,nx1,0,0,c).median();
20947           }
20948         }
20949         }
20950       }
20951       return res;
20952     }
20953 
20954     //! Sharpen image using anisotropic shock filters or inverse diffusion.
20955     CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) {
20956       if (is_empty()) return *this;
20957       T val_min, val_max = max_min(val_min);
20958       const float nedge = edge/2;
20959       CImg<Tfloat> val, vec, velocity(_width,_height,_depth,_spectrum);
20960       Tfloat *ptrd = velocity._data, veloc_max = 0;
20961 
20962       if (_depth>1) { // 3d
20963         CImg_3x3x3(I,Tfloat);
20964         if (sharpen_type) { // Shock filters.
20965           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
20966           if (sigma>0) G.blur(sigma);
20967           Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2), *ptrG3 = G.data(0,0,0,3);
20968           cimg_forXYZ(G,x,y,z) {
20969             G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
20970             if (val[0]<0) val[0] = 0;
20971             if (val[1]<0) val[1] = 0;
20972             if (val[2]<0) val[2] = 0;
20973             *(ptrG0++) = vec(0,0);
20974             *(ptrG1++) = vec(0,1);
20975             *(ptrG2++) = vec(0,2);
20976             *(ptrG3++) = 1 - (Tfloat)std::pow(1+val[0]+val[1]+val[2],-(Tfloat)nedge);
20977           }
20978           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
20979             const Tfloat
20980               u = G(x,y,z,0),
20981               v = G(x,y,z,1),
20982               w = G(x,y,z,2),
20983               amp = G(x,y,z,3),
20984               ixx = Incc + Ipcc - 2*Iccc,
20985               ixy = (Innc + Ippc - Inpc - Ipnc)/4,
20986               ixz = (Incn + Ipcp - Incp - Ipcn)/4,
20987               iyy = Icnc + Icpc - 2*Iccc,
20988               iyz = (Icnn + Icpp - Icnp - Icpn)/4,
20989               izz = Iccn + Iccp - 2*Iccc,
20990               ixf = Incc - Iccc,
20991               ixb = Iccc - Ipcc,
20992               iyf = Icnc - Iccc,
20993               iyb = Iccc - Icpc,
20994               izf = Iccn - Iccc,
20995               izb = Iccc - Iccp,
20996               itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
20997               it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
20998               veloc = -amp*cimg::sign(itt)*cimg::abs(it);
20999             *(ptrd++) = veloc;
21000             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
21001           }
21002         } else cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { // Inverse diffusion.
21003           const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
21004           *(ptrd++) = veloc;
21005           if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
21006         }
21007       } else {
21008         CImg_3x3(I,Tfloat);
21009         if (sharpen_type) { // Shock filters.
21010           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
21011           if (sigma>0) G.blur(sigma);
21012           Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2);
21013           cimg_forXY(G,x,y) {
21014             G.get_tensor_at(x,y).symmetric_eigen(val,vec);
21015             if (val[0]<0) val[0] = 0;
21016             if (val[1]<0) val[1] = 0;
21017             *(ptrG0++) = vec(0,0);
21018             *(ptrG1++) = vec(0,1);
21019             *(ptrG2++) = 1 - (Tfloat)std::pow(1+val[0]+val[1],-(Tfloat)nedge);
21020           }
21021           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
21022             const Tfloat
21023               u = G(x,y,0),
21024               v = G(x,y,1),
21025               amp = G(x,y,2),
21026               ixx = Inc + Ipc - 2*Icc,
21027               ixy = (Inn + Ipp - Inp - Ipn)/4,
21028               iyy = Icn + Icp - 2*Icc,
21029               ixf = Inc - Icc,
21030               ixb = Icc - Ipc,
21031               iyf = Icn - Icc,
21032               iyb = Icc - Icp,
21033               itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
21034               it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
21035               veloc = -amp*cimg::sign(itt)*cimg::abs(it);
21036             *(ptrd++) = veloc;
21037             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
21038           }
21039         } else cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) { // Inverse diffusion.
21040           const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
21041           *(ptrd++) = veloc;
21042           if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
21043         }
21044       }
21045       if (veloc_max<=0) return *this;
21046       return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
21047     }
21048 
21049     CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) const {
21050       return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
21051     }
21052 
21053     //! Compute the list of images, corresponding to the XY-gradients of an image.
21054     /**
21055        \param scheme = Numerical scheme used for the gradient computation :
21056        - -1 = Backward finite differences
21057        - 0 = Centered finite differences
21058        - 1 = Forward finite differences
21059        - 2 = Using Sobel masks
21060        - 3 = Using rotation invariant masks
21061        - 4 = Using Deriche recusrsive filter.
21062     **/
21063     CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=3) const {
21064       CImgList<Tfloat> grad(2,_width,_height,_depth,_spectrum);
21065       Tfloat *ptrd0 = grad[0]._data, *ptrd1 = grad[1]._data;
21066       bool is_threed = false;
21067       if (axes) {
21068         for (unsigned int a = 0; axes[a]; ++a) {
21069           const char axis = cimg::uncase(axes[a]);
21070           switch (axis) {
21071           case 'x' : case 'y' : break;
21072           case 'z' : is_threed = true; break;
21073           default :
21074             throw CImgArgumentException(_cimg_instance
21075                                         "get_gradient() : Invalid specified axis '%c'.",
21076                                         cimg_instance,
21077                                         axis);
21078           }
21079         }
21080       } else is_threed = (_depth>1);
21081       if (is_threed) {
21082         CImg<Tfloat>(_width,_height,_depth,_spectrum).move_to(grad);
21083         Tfloat *ptrd2 = grad[2]._data;
21084         switch (scheme) { // 3d.
21085         case -1 : { // Backward finite differences.
21086           CImg_3x3x3(I,Tfloat);
21087           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
21088             *(ptrd0++) = Iccc - Ipcc;
21089             *(ptrd1++) = Iccc - Icpc;
21090             *(ptrd2++) = Iccc - Iccp;
21091           }
21092         } break;
21093         case 1 : { // Forward finite differences.
21094           CImg_2x2x2(I,Tfloat);
21095           cimg_forC(*this,c) cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) {
21096             *(ptrd0++) = Incc - Iccc;
21097             *(ptrd1++) = Icnc - Iccc;
21098             *(ptrd2++) = Iccn - Iccc;
21099           }
21100         } break;
21101         case 4 : { // Using Deriche filter with low standard variation.
21102           grad[0] = get_deriche(0,1,'x');
21103           grad[1] = get_deriche(0,1,'y');
21104           grad[2] = get_deriche(0,1,'z');
21105         } break;
21106         default : { // Central finite differences.
21107           CImg_3x3x3(I,Tfloat);
21108           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
21109             *(ptrd0++) = (Incc - Ipcc)/2;
21110             *(ptrd1++) = (Icnc - Icpc)/2;
21111             *(ptrd2++) = (Iccn - Iccp)/2;
21112           }
21113         }
21114         }
21115       } else switch (scheme) { // 2d.
21116       case -1 : { // Backward finite differences.
21117         CImg_3x3(I,Tfloat);
21118         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
21119           *(ptrd0++) = Icc - Ipc;
21120           *(ptrd1++) = Icc - Icp;
21121         }
21122       } break;
21123       case 1 : { // Forward finite differences.
21124         CImg_2x2(I,Tfloat);
21125         cimg_forZC(*this,z,c) cimg_for2x2(*this,x,y,z,c,I,Tfloat) {
21126           *(ptrd0++) = Inc - Icc;
21127           *(ptrd1++) = Icn - Icc;
21128         }
21129       } break;
21130       case 2 : { // Sobel scheme.
21131         CImg_3x3(I,Tfloat);
21132         const Tfloat a = 1, b = 2;
21133         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
21134           *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
21135           *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
21136         }
21137       } break;
21138       case 3 : { // Rotation invariant mask.
21139         CImg_3x3(I,Tfloat);
21140         const Tfloat a = (Tfloat)(0.25f*(2-std::sqrt(2.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f)-1));
21141         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
21142           *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
21143           *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
21144         }
21145       } break;
21146       case 4 : { // using Deriche filter with low standard variation
21147         grad[0] = get_deriche(0,1,'x');
21148         grad[1] = get_deriche(0,1,'y');
21149       } break;
21150       default : { // central finite differences
21151         CImg_3x3(I,Tfloat);
21152         cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
21153           *(ptrd0++) = (Inc - Ipc)/2;
21154           *(ptrd1++) = (Icn - Icp)/2;
21155         }
21156       }
21157       }
21158       if (!axes) return grad;
21159       CImgList<Tfloat> res;
21160       for (unsigned int l = 0; axes[l]; ++l) {
21161         const char axis = cimg::uncase(axes[l]);
21162         switch (axis) {
21163         case 'x' : res.insert(grad[0]); break;
21164         case 'y' : res.insert(grad[1]); break;
21165         case 'z' : res.insert(grad[2]); break;
21166         }
21167       }
21168       grad.assign();
21169       return res;
21170     }
21171 
21172     //! Get components of the Hessian matrix of an image.
21173     CImgList<Tfloat> get_hessian(const char *const axes=0) const {
21174       CImgList<Tfloat> res;
21175       const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz";
21176       if (!axes) naxes = _depth>1?def_axes3d:def_axes2d;
21177       const unsigned int lmax = std::strlen(naxes);
21178       if (lmax%2)
21179         throw CImgArgumentException(_cimg_instance
21180                                     "get_hessian() : Invalid specified axes '%s'.",
21181                                     cimg_instance,
21182                                     naxes);
21183 
21184       res.assign(lmax/2,_width,_height,_depth,_spectrum);
21185       if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d
21186         Tfloat
21187           *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data,
21188           *ptrd3 = res[3]._data, *ptrd4 = res[4]._data, *ptrd5 = res[5]._data;
21189         CImg_3x3x3(I,Tfloat);
21190         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
21191           *(ptrd0++) = Ipcc + Incc - 2*Iccc;          // Ixx
21192           *(ptrd1++) = (Ippc + Innc - Ipnc - Inpc)/4; // Ixy
21193           *(ptrd2++) = (Ipcp + Incn - Ipcn - Incp)/4; // Ixz
21194           *(ptrd3++) = Icpc + Icnc - 2*Iccc;          // Iyy
21195           *(ptrd4++) = (Icpp + Icnn - Icpn - Icnp)/4; // Iyz
21196           *(ptrd5++) = Iccn + Iccp - 2*Iccc;          // Izz
21197         }
21198       } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // 2d
21199         Tfloat *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data;
21200         CImg_3x3(I,Tfloat);
21201         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
21202           *(ptrd0++) = Ipc + Inc - 2*Icc;         // Ixx
21203           *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy
21204           *(ptrd2++) = Icp + Icn - 2*Icc;         // Iyy
21205         }
21206       } else for (unsigned int l = 0; l<lmax; ) { // Version with custom axes.
21207         const unsigned int l2 = l/2;
21208           char axis1 = naxes[l++], axis2 = naxes[l++];
21209           if (axis1>axis2) cimg::swap(axis1,axis2);
21210           bool valid_axis = false;
21211           Tfloat *ptrd = res[l2]._data;
21212           if (axis1=='x' && axis2=='x') { // Ixx
21213             valid_axis = true; CImg_3x3(I,Tfloat);
21214             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc;
21215           }
21216           else if (axis1=='x' && axis2=='y') { // Ixy
21217             valid_axis = true; CImg_3x3(I,Tfloat);
21218             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4;
21219           }
21220           else if (axis1=='x' && axis2=='z') { // Ixz
21221             valid_axis = true; CImg_3x3x3(I,Tfloat);
21222             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4;
21223           }
21224           else if (axis1=='y' && axis2=='y') { // Iyy
21225             valid_axis = true; CImg_3x3(I,Tfloat);
21226             cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc;
21227           }
21228           else if (axis1=='y' && axis2=='z') { // Iyz
21229             valid_axis = true; CImg_3x3x3(I,Tfloat);
21230             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4;
21231           }
21232           else if (axis1=='z' && axis2=='z') { // Izz
21233             valid_axis = true; CImg_3x3x3(I,Tfloat);
21234             cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc;
21235           }
21236           else if (!valid_axis)
21237             throw CImgArgumentException(_cimg_instance
21238                                         "get_hessian() : Invalid specified axes '%s'.",
21239                                         cimg_instance,
21240                                         naxes);
21241         }
21242       return res;
21243     }
21244 
21245     //! Compute the laplacian of the instance image.
21246     CImg<T>& laplacian() {
21247       return get_laplacian().move_to(*this);
21248     }
21249 
21250     CImg<Tfloat> get_laplacian() const {
21251       if (is_empty()) return CImg<Tfloat>();
21252       CImg<Tfloat> res(_width,_height,_depth,_spectrum);
21253       Tfloat *ptrd = res._data;
21254       if (_depth>1) { // 3d
21255         CImg_3x3x3(I,Tfloat);
21256         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat)
21257           *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
21258       } else if (_height>1) { // 2d
21259         CImg_3x3(I,Tfloat);
21260         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat)
21261           *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
21262       } else { // 1d
21263         CImg_3x3(I,Tfloat);
21264         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat)
21265           *(ptrd++) = Inc + Ipc - 2*Icc;
21266       }
21267       return res;
21268     }
21269 
21270     //! Compute the structure tensor field of an image.
21271     CImg<T>& structure_tensors(const unsigned int scheme=1) {
21272       return get_structure_tensors(scheme).move_to(*this);
21273     }
21274 
21275     CImg<Tfloat> get_structure_tensors(const unsigned int scheme=1) const {
21276       if (is_empty()) return *this;
21277       CImg<Tfloat> res;
21278       if (_depth>1) { // 3d
21279         res.assign(_width,_height,_depth,6,0);
21280         CImg_3x3x3(I,Tfloat);
21281         switch (scheme) {
21282         case 0 : { // classical central finite differences
21283           cimg_forC(*this,c) {
21284             Tfloat
21285               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
21286               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
21287             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
21288               const Tfloat
21289                 ix = (Incc - Ipcc)/2,
21290                 iy = (Icnc - Icpc)/2,
21291                 iz = (Iccn - Iccp)/2;
21292               *(ptrd0++)+=ix*ix;
21293               *(ptrd1++)+=ix*iy;
21294               *(ptrd2++)+=ix*iz;
21295               *(ptrd3++)+=iy*iy;
21296               *(ptrd4++)+=iy*iz;
21297               *(ptrd5++)+=iz*iz;
21298             }
21299           }
21300         } break;
21301         case 1 : { // Forward/backward finite differences (version 1).
21302           cimg_forC(*this,c) {
21303             Tfloat
21304               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
21305               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
21306             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
21307               const Tfloat
21308                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
21309                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
21310                 izf = Iccn - Iccc, izb = Iccc - Iccp;
21311               *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*ixf + ixb*ixb)/4;
21312               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
21313               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
21314               *(ptrd3++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4;
21315               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
21316               *(ptrd5++)+=(izf*izf + izf*izb + izb*izf + izb*izb)/4;
21317             }
21318           }
21319         } break;
21320         default : { // Forward/backward finite differences (version 2).
21321           cimg_forC(*this,c) {
21322             Tfloat
21323               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
21324               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
21325             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
21326               const Tfloat
21327                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
21328                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
21329                 izf = Iccn - Iccc, izb = Iccc - Iccp;
21330               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
21331               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
21332               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
21333               *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
21334               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
21335               *(ptrd5++)+=(izf*izf + izb*izb)/2;
21336             }
21337           }
21338         } break;
21339         }
21340       } else { // 2d
21341         res.assign(_width,_height,_depth,3,0);
21342         CImg_3x3(I,Tfloat);
21343         switch (scheme) {
21344         case 0 : { // classical central finite differences
21345           cimg_forC(*this,c) {
21346             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
21347             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
21348               const Tfloat
21349                 ix = (Inc - Ipc)/2,
21350                 iy = (Icn - Icp)/2;
21351               *(ptrd0++)+=ix*ix;
21352               *(ptrd1++)+=ix*iy;
21353               *(ptrd2++)+=iy*iy;
21354             }
21355           }
21356         } break;
21357         case 1 : { // Forward/backward finite differences (version 1).
21358           cimg_forC(*this,c) {
21359             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
21360             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
21361               const Tfloat
21362                 ixf = Inc - Icc, ixb = Icc - Ipc,
21363                 iyf = Icn - Icc, iyb = Icc - Icp;
21364               *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*iyf + ixb*ixb)/4;
21365               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
21366               *(ptrd2++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4;
21367             }
21368           }
21369         } break;
21370         default : { // Forward/backward finite differences (version 2).
21371           cimg_forC(*this,c) {
21372             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
21373             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
21374               const Tfloat
21375                 ixf = Inc - Icc, ixb = Icc - Ipc,
21376                 iyf = Icn - Icc, iyb = Icc - Icp;
21377               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
21378               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
21379               *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
21380             }
21381           }
21382         } break;
21383         }
21384       }
21385       return res;
21386     }
21387 
21388     //! Get a diffusion tensor for edge-preserving anisotropic smoothing of an image.
21389     CImg<T>& edge_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
21390                           const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
21391       CImg<Tfloat> res;
21392       const float nsharpness = cimg::max(sharpness,1e-5f), power1 = (is_sqrt?0.5f:1)*nsharpness, power2 = power1/(1e-7f+1-anisotropy);
21393       blur(alpha).normalize(0,(T)255);
21394 
21395       if (_depth>1) { // 3d
21396         CImg<floatT> val(3), vec(3,3);
21397         get_structure_tensors().move_to(res).blur(sigma);
21398         Tfloat
21399           *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
21400           *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
21401         cimg_forXYZ(*this,x,y,z) {
21402           res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
21403           const float
21404             _l1 = val[2], _l2 = val[1], _l3 = val[0],
21405             l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
21406             ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
21407             vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
21408             wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
21409             n1 = (float)std::pow(1+l1+l2+l3,-power1),
21410             n2 = (float)std::pow(1+l1+l2+l3,-power2);
21411           *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
21412           *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
21413           *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
21414           *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
21415           *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
21416           *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
21417         }
21418       } else { // for 2d images
21419         CImg<floatT> val(2), vec(2,2);
21420         get_structure_tensors().move_to(res).blur(sigma);
21421         Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
21422         cimg_forXY(*this,x,y) {
21423           res.get_tensor_at(x,y).symmetric_eigen(val,vec);
21424           const float
21425             _l1 = val[1], _l2 = val[0],
21426             l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
21427             ux = vec(1,0), uy = vec(1,1),
21428             vx = vec(0,0), vy = vec(0,1),
21429             n1 = (float)std::pow(1+l1+l2,-power1),
21430             n2 = (float)std::pow(1+l1+l2,-power2);
21431           *(ptrd0++) = n1*ux*ux + n2*vx*vx;
21432           *(ptrd1++) = n1*ux*uy + n2*vx*vy;
21433           *(ptrd2++) = n1*uy*uy + n2*vy*vy;
21434         }
21435       }
21436       return res.move_to(*this);
21437     }
21438 
21439     CImg<Tfloat> get_edge_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
21440                                   const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
21441       return CImg<Tfloat>(*this,false).edge_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
21442     }
21443 
21444     //! Estimate a displacement field between instance image and given target image.
21445     /**
21446        \param backward : if false, match I2(X+U(X)) = I1(X), else match I2(X) = I1(X-U(X)).
21447     **/
21448     CImg<T>& displacement(const CImg<T>& target, const float smooth=0.1f, const float precision=0.1f,
21449                           const unsigned int nb_scales=0, const unsigned int iteration_max=1000,
21450                           const bool backward = true) {
21451       return get_displacement(target,smooth,precision,nb_scales,iteration_max,backward).move_to(*this);
21452     }
21453 
21454     CImg<Tfloat> get_displacement(const CImg<T>& target,
21455                                   const float smoothness=0.1f, const float precision=0.1f,
21456                                   const unsigned int nb_scales=0, const unsigned int iteration_max=1000,
21457                                   const bool backward = true) const {
21458       if (is_empty() || !target) return *this;
21459       if (!is_sameXYZC(target))
21460         throw CImgArgumentException(_cimg_instance
21461                                     "displacement() : Instance and target image (%u,%u,%u,%u,%p) have different dimensions.",
21462                                     cimg_instance,
21463                                     target._width,target._height,target._depth,target._spectrum,target._data);
21464       if (smoothness<0)
21465         throw CImgArgumentException(_cimg_instance
21466                                     "displacement() : Invalid specified smoothness %g "
21467                                     "(should be >=0",
21468                                     cimg_instance,
21469                                     smoothness);
21470       if (precision<0)
21471         throw CImgArgumentException(_cimg_instance
21472                                     "displacement() : Invalid specified precision %g "
21473                                     "(should be >=0",
21474                                     cimg_instance,
21475                                     precision);
21476 
21477       const unsigned int nscales = nb_scales>0?nb_scales:(unsigned int)(2*std::log((double)(cimg::max(_width,_height,_depth))));
21478       Tfloat m1, M1 = (Tfloat)max_min(m1), m2, M2 = (Tfloat)target.max_min(m2);
21479       const Tfloat factor = cimg::max(cimg::abs(m1),cimg::abs(M1),cimg::abs(m2),cimg::abs(M2));
21480       CImg<Tfloat> U0;
21481       const bool is_threed = (_depth>1);
21482 
21483       // Begin multi-scale motion estimation
21484       for (int scale = (int)nscales-1; scale>=0; --scale) {
21485         const float sfactor = (float)std::pow(1.5f,(float)scale), sprecision = (float)(precision/std::pow(2.25,1+scale));
21486         const int
21487           sw = (int)(_width/sfactor), sh = (int)(_height/sfactor), sd = (int)(_depth/sfactor),
21488           swidth = sw?sw:1, sheight = sh?sh:1, sdepth = sd?sd:1;
21489         CImg<Tfloat>
21490           I1 = get_resize(swidth,sheight,sdepth,-100,2),
21491           I2 = target.get_resize(swidth,sheight,sdepth,-100,2);
21492         I1/=factor; I2/=factor;
21493         CImg<Tfloat> U;
21494         if (U0) U = (U0*=1.5f).get_resize(I1.width(),I1.height(),I1.depth(),-100,3);
21495         else U.assign(I1.width(),I1.height(),I1.depth(),is_threed?3:2,0);
21496 
21497         // Begin single-scale motion estimation
21498         CImg<Tfloat> veloc(U);
21499         float dt = 2, energy = cimg::type<float>::max();
21500         const CImgList<Tfloat> dI = backward?I1.get_gradient():I2.get_gradient();
21501         for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
21502           veloc.fill(0);
21503           float nenergy = 0;
21504           if (is_threed) {
21505             cimg_for3XYZ(U,x,y,z) {
21506               const float
21507                 sgnU = backward?-1.0f:1.0f,
21508                 X = (float)(x + sgnU * U(x,y,z,0)),
21509                 Y = (float)(y + sgnU * U(x,y,z,1)),
21510                 Z = (float)(z + sgnU * U(x,y,z,2));
21511               cimg_forC(U,c) {
21512                 const Tfloat
21513                   Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
21514                   Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
21515                   Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
21516                   Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c) - 2*U(x,y,z,c),
21517                   Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c) - 2*U(x,y,z,c),
21518                   Uzz = U(x,y,_n1z,c) + U(x,y,_n1z,c) - 2*U(x,y,z,c);
21519                 nenergy+=(float)(smoothness*(Ux*Ux + Uy*Uy + Uz*Uz));
21520                 Tfloat deltaIgrad = 0;
21521                 cimg_forC(I1,i) {
21522                   const Tfloat deltaIi = (float)(backward?I2(x,y,z,i)-I1._linear_atXYZ(X,Y,Z,i):I2._linear_atXYZ(X,Y,Z,i)-I1(x,y,z,i));
21523                   nenergy+=(float)(deltaIi*deltaIi/2);
21524                   deltaIgrad+=-deltaIi*dI[c]._linear_atXYZ(X,Y,Z,i);
21525                 }
21526                 veloc(x,y,z,c) = deltaIgrad + smoothness*(Uxx + Uyy + Uzz);
21527               }
21528             }
21529           } else {
21530             cimg_for3XY(U,x,y) {
21531               const float
21532                 sgnU = backward?-1.0f:1.0f,
21533                 X = (float)(x + sgnU * U(x,y,0)),
21534                 Y = (float)(y + sgnU * U(x,y,1));
21535               cimg_forC(U,c) {
21536                 const Tfloat
21537                   Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
21538                   Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
21539                   Uxx = U(_n1x,y,c) + U(_p1x,y,c) - 2*U(x,y,c),
21540                   Uyy = U(x,_n1y,c) + U(x,_p1y,c) - 2*U(x,y,c);
21541                 nenergy+=(float)(smoothness*(Ux*Ux + Uy*Uy));
21542                 Tfloat deltaIgrad = 0;
21543                 cimg_forC(I1,i) {
21544                   const Tfloat deltaIi = (float)(backward?I2(x,y,i)-I1.linear_atXY(X,Y,i):I2._linear_atXY(X,Y,i)-I1(x,y,i));
21545                   nenergy+=(float)(deltaIi*deltaIi/2);
21546                   deltaIgrad+=-deltaIi*dI[c]._linear_atXY(X,Y,i);
21547                 }
21548                 veloc(x,y,c) = deltaIgrad + smoothness*(Uxx + Uyy);
21549               }
21550             }
21551           }
21552           const Tfloat vmax = cimg::max(cimg::abs(veloc.min()), cimg::abs(veloc.max()));
21553           U+=(veloc*=dt/(1e-6+vmax));
21554           if (cimg::abs(nenergy-energy)<sprecision) break;
21555           if (nenergy<energy) dt*=0.5f;
21556           energy = nenergy;
21557         }
21558         U.move_to(U0);
21559       }
21560       return U0;
21561     }
21562 
21563     //! Compute the Euclidean distance map to a shape of specified isovalue.
21564     CImg<T>& distance(const T isovalue,
21565                       const float sizex=1, const float sizey=1, const float sizez=1,
21566                       const bool compute_sqrt=true) {
21567       return get_distance(isovalue,sizex,sizey,sizez,compute_sqrt).move_to(*this);
21568     }
21569 
21570     CImg<floatT> get_distance(const T isovalue,
21571                               const float sizex=1, const float sizey=1, const float sizez=1,
21572                               const bool compute_sqrt=true) const {
21573       if (is_empty()) return *this;
21574       const int dx = width(), dy = height(), dz = depth();
21575       CImg<floatT> res(dx,dy,dz,_spectrum);
21576       const float maxdist = (float)std::sqrt((float)dx*dx + dy*dy + dz*dz);
21577       cimg_forC(*this,c) {
21578         bool is_isophote = false;
21579 
21580         if (_depth>1) { // 3d version
21581           cimg_forYZ(*this,y,z) {
21582             if ((*this)(0,y,z,c)==isovalue) { is_isophote = true; res(0,y,z,c) = 0; } else res(0,y,z,c) = maxdist;
21583             for (int x = 1; x<dx; ++x) if ((*this)(x,y,z,c)==isovalue) { is_isophote = true; res(x,y,z,c) = 0; }
21584             else res(x,y,z,c) = res(x-1,y,z,c) + sizex;
21585             for (int x = dx-2; x>=0; --x) if (res(x+1,y,z,c)<res(x,y,z,c)) res(x,y,z,c) = res(x+1,y,z,c) + sizex;
21586           }
21587           if (!is_isophote) { res.get_shared_channel(c).fill(cimg::type<floatT>::max()); continue; }
21588           CImg<floatT> tmp(cimg::max(dy,dz));
21589           CImg<intT> s(tmp._width), t(s._width);
21590           cimg_forXZ(*this,x,z) {
21591             cimg_forY(*this,y) tmp[y] = res(x,y,z,c);
21592             int q = s[0] = t[0] = 0;
21593             for (int y = 1; y<dy; ++y) {
21594               const float val = tmp[y], val2 = val*val;
21595               while (q>=0 && _distance_f(t[q],s[q],cimg::sqr(tmp[s[q]]),sizey)>_distance_f(t[q],y,val2,sizey)) --q;
21596               if (q<0) { q = 0; s[0] = y; }
21597               else {
21598                 const int w = 1 + _distance_sep(s[q],y,(int)cimg::sqr(tmp[s[q]]),(int)val2,sizey);
21599                 if (w<dy) { s[++q] = y; t[q] = w; }
21600               }
21601             }
21602             for (int y = dy - 1; y>=0; --y) {
21603               res(x,y,z,c) = _distance_f(y,s[q],cimg::sqr(tmp[s[q]]),sizey);
21604               if (y==t[q]) --q;
21605             }
21606           }
21607           cimg_forXY(*this,x,y) {
21608             cimg_forZ(*this,z) tmp[z] = res(x,y,z,c);
21609             int q = s[0] = t[0] = 0;
21610             for (int z = 1; z<dz; ++z) {
21611               const float val = tmp[z];
21612               while (q>=0 && _distance_f(t(q),s[q],tmp[s[q]],sizez)>_distance_f(t[q],z,tmp[z],sizez)) --q;
21613               if (q<0) { q = 0; s[0] = z; }
21614               else {
21615                 const int w = 1 + _distance_sep(s[q],z,(int)tmp[s[q]],(int)val,sizez);
21616                 if (w<dz) { s[++q] = z; t[q] = w; }
21617               }
21618             }
21619             for (int z = dz - 1; z>=0; --z) {
21620               const float val = _distance_f(z,s[q],tmp[s[q]],sizez);
21621               res(x,y,z,c) = compute_sqrt?(float)std::sqrt(val):val;
21622               if (z==t[q]) --q;
21623             }
21624           }
21625         } else { // 2d version (with small optimizations)
21626           cimg_forX(*this,x) {
21627             const T *ptrs = data(x,0,0,c);
21628             float *ptrd = res.data(x,0,0,c), d = *ptrd = *ptrs==isovalue?(is_isophote=true),0:maxdist;
21629             for (int y = 1; y<dy; ++y) { ptrs+=_width; ptrd+=_width; d = *ptrd = *ptrs==isovalue?(is_isophote=true),0:d+sizey; }
21630             for (int y = dy - 2; y>=0; --y) { ptrd-=_width; if (d<*ptrd) *ptrd = (d+=sizey); else d = *ptrd; }
21631           }
21632           if (!is_isophote) { res.get_shared_channel(c).fill(cimg::type<floatT>::max()); continue; }
21633           CImg<floatT> tmp(dx);
21634           CImg<intT> s(dx), t(dx);
21635           cimg_forY(*this,y) {
21636             float *ptmp = tmp._data;
21637             std::memcpy(ptmp,res.data(0,y,0,c),sizeof(float)*dx);
21638             int q = s[0] = t[0] = 0;
21639             for (int x = 1; x<dx; ++x) {
21640               const float val = *(++ptmp), val2 = val*val;
21641               while (q>=0 && _distance_f(t[q],s[q],cimg::sqr(tmp[s[q]]),sizex)>_distance_f(t[q],x,val2,sizex)) --q;
21642               if (q<0) { q = 0; s[0] = x; }
21643               else {
21644                 const int w = 1 + _distance_sep(s[q],x,(int)cimg::sqr(tmp[s[q]]),(int)val2,sizex);
21645                 if (w<dx) { s[++q] = x; t[q] = w; }
21646               }
21647             }
21648             float *pres = res.data(0,y,0,c) + _width;
21649             for (int x = dx - 1; x>=0; --x) {
21650               const float val = _distance_f(x,s[q],cimg::sqr(tmp[s[q]]),sizex);
21651               *(--pres) = compute_sqrt?(float)std::sqrt(val):val;
21652               if (x==t[q]) --q;
21653             }
21654           }
21655         }
21656       }
21657       return res;
21658     }
21659 
21660     static float _distance_f(const int x, const int i, const float gi2, const float fact) {
21661       const float xmi = fact*((float)x - i);
21662       return xmi*xmi + gi2;
21663     }
21664     static int _distance_sep(const int i, const int u, const int gi2, const int gu2, const float fact) {
21665       const float fact2 = fact*fact;
21666       return (int)(fact2*(u*u - i*i) + gu2 - gi2)/(int)(2*fact2*(u - i));
21667     }
21668 
21669     //! Compute distance function from 0-valued isophotes by the application of an Eikonal PDE.
21670     CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
21671       if (is_empty()) return *this;
21672       CImg<Tfloat> velocity(*this);
21673       for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
21674         Tfloat *ptrd = velocity._data, veloc_max = 0;
21675         if (_depth>1) { // 3d
21676           CImg_3x3x3(I,Tfloat);
21677           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
21678             const Tfloat
21679               gx = (Incc - Ipcc)/2,
21680               gy = (Icnc - Icpc)/2,
21681               gz = (Iccn - Iccp)/2,
21682               sgn = -cimg::sign(Iccc),
21683               ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
21684               iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
21685               iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
21686               ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy + gz*gz)),
21687               ngx = gx/ng,
21688               ngy = gy/ng,
21689               ngz = gz/ng,
21690               veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
21691             *(ptrd++) = veloc;
21692             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
21693           } else *(ptrd++) = 0;
21694         } else { // 2d version
21695           CImg_3x3(I,Tfloat);
21696           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
21697             const Tfloat
21698               gx = (Inc - Ipc)/2,
21699               gy = (Icn - Icp)/2,
21700               sgn = -cimg::sign(Icc),
21701               ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
21702               iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
21703               ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy)),
21704               ngx = gx/ng,
21705               ngy = gy/ng,
21706               veloc = sgn*(ngx*ix + ngy*iy - 1);
21707             *(ptrd++) = veloc;
21708             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
21709           } else *(ptrd++) = 0;
21710         }
21711         if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
21712       }
21713       return *this;
21714     }
21715 
21716     CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) const {
21717       return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
21718     }
21719 
21720     //! Compute the Haar multiscale wavelet transform (monodimensional version).
21721     /**
21722        \param axis Axis considered for the transform.
21723        \param invert Set inverse of direct transform.
21724        \param nb_scales Number of scales used for the transform.
21725     **/
21726     CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
21727       return get_haar(axis,invert,nb_scales).move_to(*this);
21728     }
21729 
21730     CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
21731       if (is_empty() || !nb_scales) return *this;
21732       CImg<Tfloat> res;
21733 
21734       if (nb_scales==1) {
21735         switch (cimg::uncase(axis)) { // Single scale transform
21736         case 'x' : {
21737           const unsigned int w = _width/2;
21738           if (w) {
21739             if (w%2)
21740               throw CImgInstanceException(_cimg_instance
21741                                           "haar() : Sub-image width %u is not even at scale %u.",
21742                                           cimg_instance,
21743                                           w);
21744 
21745             res.assign(_width,_height,_depth,_spectrum);
21746             if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
21747               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
21748                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
21749                 res(x2++,y,z,c) = val0 - val1;
21750                 res(x2++,y,z,c) = val0 + val1;
21751               }
21752             } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
21753               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
21754                 const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
21755                 res(x,y,z,c) = (val0 + val1)/2;
21756                 res(xw,y,z,c) = (val1 - val0)/2;
21757               }
21758             }
21759           } else return *this;
21760         } break;
21761         case 'y' : {
21762           const unsigned int h = _height/2;
21763           if (h) {
21764             if (h%2)
21765               throw CImgInstanceException(_cimg_instance
21766                                           "haar() : Sub-image height %u is not even at scale %u.",
21767                                           cimg_instance,
21768                                           h);
21769 
21770             res.assign(_width,_height,_depth,_spectrum);
21771             if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
21772               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
21773                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
21774                 res(x,y2++,z,c) = val0 - val1;
21775                 res(x,y2++,z,c) = val0 + val1;
21776               }
21777             } else cimg_forXZC(*this,x,z,c) {
21778               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
21779                 const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
21780                 res(x,y,z,c) = (val0 + val1)/2;
21781                 res(x,yh,z,c) = (val1 - val0)/2;
21782               }
21783             }
21784           } else return *this;
21785         } break;
21786         case 'z' : {
21787           const unsigned int d = _depth/2;
21788           if (d) {
21789             if (d%2)
21790               throw CImgInstanceException(_cimg_instance
21791                                           "haar() : Sub-image depth %u is not even at scale %u.",
21792                                           cimg_instance,
21793                                           d);
21794 
21795             res.assign(_width,_height,_depth,_spectrum);
21796             if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
21797               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
21798                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
21799                 res(x,y,z2++,c) = val0 - val1;
21800                 res(x,y,z2++,c) = val0 + val1;
21801               }
21802             } else cimg_forXYC(*this,x,y,c) {
21803               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
21804                 const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
21805                 res(x,y,z,c) = (val0 + val1)/2;
21806                 res(x,y,zd,c) = (val1 - val0)/2;
21807               }
21808             }
21809           } else return *this;
21810         } break;
21811         default :
21812           throw CImgArgumentException(_cimg_instance
21813                                       "haar() : Invalid specified axis '%c' "
21814                                       "(should be { x | y | z }).",
21815                                       cimg_instance,
21816                                       axis);
21817         }
21818       } else { // Multi-scale version
21819         if (invert) {
21820           res.assign(*this);
21821           switch (cimg::uncase(axis)) {
21822           case 'x' : {
21823             unsigned int w = _width;
21824             for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
21825             for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',true,1));
21826           } break;
21827           case 'y' : {
21828             unsigned int h = _width;
21829             for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
21830             for (h = h?h:1; h<=_height; h*=2) res.draw_image(res.get_crop(0,0,_width-1,h-1).get_haar('y',true,1));
21831           } break;
21832           case 'z' : {
21833             unsigned int d = _depth;
21834             for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
21835             for (d = d?d:1; d<=_depth; d*=2) res.draw_image(res.get_crop(0,0,0,_width-1,_height-1,d-1).get_haar('z',true,1));
21836           } break;
21837           default :
21838             throw CImgArgumentException(_cimg_instance
21839                                         "haar() : Invalid specified axis '%c' "
21840                                         "(should be { x | y | z }).",
21841                                         cimg_instance,
21842                                         axis);
21843           }
21844         } else { // Direct transform
21845           res = get_haar(axis,false,1);
21846           switch (cimg::uncase(axis)) {
21847           case 'x' : {
21848             for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',false,1));
21849           } break;
21850           case 'y' : {
21851             for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2) res.draw_image(res.get_crop(0,0,_width-1,h-1).get_haar('y',false,1));
21852           } break;
21853           case 'z' : {
21854             for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2) res.draw_image(res.get_crop(0,0,0,_width-1,_height-1,d-1).get_haar('z',false,1));
21855           } break;
21856           default :
21857             throw CImgArgumentException(_cimg_instance
21858                                         "haar() : Invalid specified axis '%c' "
21859                                         "(should be { x | y | z }).",
21860                                         cimg_instance,
21861                                         axis);
21862           }
21863         }
21864       }
21865       return res;
21866     }
21867 
21868     //! Compute the Haar multiscale wavelet transform.
21869     /**
21870        \param invert Set inverse of direct transform.
21871        \param nb_scales Number of scales used for the transform.
21872     **/
21873     CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
21874       return get_haar(invert,nb_scales).move_to(*this);
21875     }
21876 
21877     CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
21878       CImg<Tfloat> res;
21879 
21880       if (nb_scales==1) { // Single scale transform
21881         if (_width>1) get_haar('x',invert,1).move_to(res);
21882         if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
21883         if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
21884         if (res) return res;
21885       } else { // Multi-scale transform
21886         if (invert) { // Inverse transform
21887           res.assign(*this);
21888           if (_width>1) {
21889             if (_height>1) {
21890               if (_depth>1) {
21891                 unsigned int w = _width, h = _height, d = _depth; for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
21892                 for (w = w?w:1, h = h?h:1, d = d?d:1; w<=_width && h<=_height && d<=_depth; w*=2, h*=2, d*=2)
21893                   res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).get_haar(true,1));
21894               } else {
21895                 unsigned int w = _width, h = _height; for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
21896                 for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
21897                   res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).get_haar(true,1));
21898               }
21899             } else {
21900               if (_depth>1) {
21901                 unsigned int w = _width, d = _depth; for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
21902                 for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
21903                   res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).get_haar(true,1));
21904               } else {
21905                 unsigned int w = _width; for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
21906                 for (w = w?w:1; w<=_width; w*=2)
21907                   res.draw_image(res.get_crop(0,0,0,w-1,0,0).get_haar(true,1));
21908               }
21909             }
21910           } else {
21911             if (_height>1) {
21912               if (_depth>1) {
21913                 unsigned int h = _height, d = _depth; for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
21914                 for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
21915                   res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).get_haar(true,1));
21916               } else {
21917                 unsigned int h = _height; for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
21918                 for (h = h?h:1; h<=_height; h*=2)
21919                   res.draw_image(res.get_crop(0,0,0,0,h-1,0).get_haar(true,1));
21920               }
21921             } else {
21922               if (_depth>1) {
21923                 unsigned int d = _depth; for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
21924                 for (d = d?d:1; d<=_depth; d*=2)
21925                   res.draw_image(res.get_crop(0,0,0,0,0,d-1).get_haar(true,1));
21926               } else return *this;
21927             }
21928           }
21929         } else { // Direct transform
21930           res = get_haar(false,1);
21931           if (_width>1) {
21932             if (_height>1) {
21933               if (_depth>1) for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales; ++s, w/=2, h/=2, d/=2)
21934                 res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).haar(false,1));
21935               else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
21936                 res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).haar(false,1));
21937             } else {
21938               if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
21939                 res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).haar(false,1));
21940               else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
21941                 res.draw_image(res.get_crop(0,0,0,w-1,0,0).haar(false,1));
21942             }
21943           } else {
21944             if (_height>1) {
21945               if (_depth>1) for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
21946                 res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).haar(false,1));
21947               else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
21948                 res.draw_image(res.get_crop(0,0,0,0,h-1,0).haar(false,1));
21949             } else {
21950               if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
21951                 res.draw_image(res.get_crop(0,0,0,0,0,d-1).haar(false,1));
21952               else return *this;
21953             }
21954           }
21955         }
21956         return res;
21957       }
21958       return *this;
21959     }
21960 
21961     //! Compute a 1d Fast Fourier Transform, along a specified axis.
21962     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
21963       CImgList<Tfloat> res(*this,CImg<Tfloat>());
21964       CImg<Tfloat>::FFT(res[0],res[1],axis,invert);
21965       return res;
21966     }
21967 
21968     //! Compute a n-d Fast-Fourier Transform.
21969     CImgList<Tfloat> get_FFT(const bool invert=false) const {
21970       CImgList<Tfloat> res(*this,CImg<Tfloat>());
21971       CImg<Tfloat>::FFT(res[0],res[1],invert);
21972       return res;
21973     }
21974 
21975     //! Compute a 1d Fast Fourier Transform, along a specified axis.
21976     static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool invert=false) {
21977       if (!real)
21978         throw CImgInstanceException("CImg<%s>::FFT() : Specified real part is empty.",
21979                                     pixel_type());
21980 
21981       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
21982       if (!real.is_sameXYZC(imag))
21983         throw CImgInstanceException("CImg<%s>::FFT() : Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
21984                                     pixel_type(),
21985                                     real._width,real._height,real._depth,real._spectrum,real._data,
21986                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
21987 #ifdef cimg_use_fftw3
21988       fftw_complex *data_in;
21989       fftw_plan data_plan;
21990 
21991       switch (cimg::uncase(axis)) {
21992       case 'x' : { // Fourier along X, using FFTW library.
21993         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width);
21994         data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
21995         cimg_forYZC(real,y,z,c) {
21996           T *ptrr = real.data(0,y,z,c), *ptri = imag.data(0,y,z,c);
21997           double *ptrd = (double*)data_in;
21998           cimg_forX(real,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); }
21999           fftw_execute(data_plan);
22000           const unsigned int fact = real._width;
22001           if (invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); }
22002           else cimg_forX(real,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); }
22003         }
22004       } break;
22005       case 'y' : { // Fourier along Y, using FFTW library.
22006         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._height);
22007         data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
22008         const unsigned int off = real._width;
22009         cimg_forXZC(real,x,z,c) {
22010           T *ptrr = real.data(x,0,z,c), *ptri = imag.data(x,0,z,c);
22011           double *ptrd = (double*)data_in;
22012           cimg_forY(real,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
22013           fftw_execute(data_plan);
22014           const unsigned int fact = real._height;
22015           if (invert) cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
22016           else cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
22017         }
22018       } break;
22019       case 'z' : { // Fourier along Z, using FFTW library.
22020         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._depth);
22021         data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
22022         const unsigned int off = real._width*real._height;
22023         cimg_forXYC(real,x,y,c) {
22024           T *ptrr = real.data(x,y,0,c), *ptri = imag.data(x,y,0,c);
22025           double *ptrd = (double*)data_in;
22026           cimg_forZ(real,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
22027           fftw_execute(data_plan);
22028           const unsigned int fact = real._depth;
22029           if (invert) cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
22030           else cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
22031         }
22032       } break;
22033       default : { // Fourier along C, using FFTW library.
22034         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._spectrum);
22035         data_plan = fftw_plan_dft_1d(real._spectrum,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
22036         const unsigned int off = real._width*real._height*real._depth;
22037         cimg_forXYZ(real,x,y,z) {
22038           T *ptrr = real.data(x,y,z,0), *ptri = imag.data(x,y,z,0);
22039           double *ptrd = (double*)data_in;
22040           cimg_forC(real,c) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
22041           fftw_execute(data_plan);
22042           const unsigned int fact = real._spectrum;
22043           if (invert) cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
22044           else cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
22045         }
22046       }
22047       }
22048       fftw_destroy_plan(data_plan);
22049       fftw_free(data_in);
22050 #else
22051       switch (cimg::uncase(axis)) {
22052       case 'x' : { // Fourier along X, using built-in functions.
22053         const unsigned int N = real._width, N2 = (N>>1);
22054         if (((N-1)&N) && N!=1)
22055           throw CImgInstanceException("CImgList<%s>::FFT() : Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the X-axis.",
22056                                       pixel_type(),
22057                                       real._width,real._height,real._depth,real._spectrum);
22058 
22059         for (unsigned int i = 0, j = 0; i<N2; ++i) {
22060           if (j>i) cimg_forYZC(real,y,z,c) {
22061             cimg::swap(real(i,y,z,c),real(j,y,z,c)); cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
22062             if (j<N2) {
22063               const unsigned int ri = N-1-i, rj = N-1-j;
22064               cimg::swap(real(ri,y,z,c),real(rj,y,z,c)); cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
22065             }
22066           }
22067           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
22068         }
22069         for (unsigned int delta = 2; delta<=N; delta<<=1) {
22070           const unsigned int delta2 = (delta>>1);
22071           for (unsigned int i = 0; i<N; i+=delta) {
22072             float wr = 1, wi = 0;
22073             const float angle = (float)((invert?+1:-1)*2*cimg::PI/delta),
22074                         ca = (float)std::cos(angle),
22075                         sa = (float)std::sin(angle);
22076             for (unsigned int k = 0; k<delta2; ++k) {
22077               const unsigned int j = i + k, nj = j + delta2;
22078               cimg_forYZC(real,y,z,c) {
22079                 T &ir = real(j,y,z,c), &ii = imag(j,y,z,c), &nir = real(nj,y,z,c), &nii = imag(nj,y,z,c);
22080                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
22081                 nir = (T)(ir - tmpr);
22082                 nii = (T)(ii - tmpi);
22083                 ir+=(T)tmpr;
22084                 ii+=(T)tmpi;
22085               }
22086               const float nwr = wr*ca-wi*sa;
22087               wi = wi*ca + wr*sa;
22088               wr = nwr;
22089             }
22090           }
22091         }
22092         if (invert) { real/=N; imag/=N; }
22093       } break;
22094       case 'y' : { // Fourier along Y, using built-in functions.
22095         const unsigned int N = real._height, N2 = (N>>1);
22096         if (((N-1)&N) && N!=1)
22097           throw CImgInstanceException("CImgList<%s>::FFT() : Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Y-axis.",
22098                                       pixel_type(),
22099                                       real._width,real._height,real._depth,real._spectrum);
22100 
22101         for (unsigned int i = 0, j = 0; i<N2; ++i) {
22102           if (j>i) cimg_forXZC(real,x,z,c) {
22103             cimg::swap(real(x,i,z,c),real(x,j,z,c)); cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
22104             if (j<N2) {
22105               const unsigned int ri = N - 1 - i, rj = N - 1 - j;
22106               cimg::swap(real(x,ri,z,c),real(x,rj,z,c)); cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
22107             }
22108           }
22109           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
22110         }
22111         for (unsigned int delta = 2; delta<=N; delta<<=1) {
22112           const unsigned int delta2 = (delta>>1);
22113           for (unsigned int i = 0; i<N; i+=delta) {
22114             float wr = 1, wi = 0;
22115             const float angle = (float)((invert?+1:-1)*2*cimg::PI/delta),
22116                         ca = (float)std::cos(angle), sa = (float)std::sin(angle);
22117             for (unsigned int k = 0; k<delta2; ++k) {
22118               const unsigned int j = i + k, nj = j + delta2;
22119               cimg_forXZC(real,x,z,c) {
22120                 T &ir = real(x,j,z,c), &ii = imag(x,j,z,c), &nir = real(x,nj,z,c), &nii = imag(x,nj,z,c);
22121                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
22122                 nir = (T)(ir - tmpr);
22123                 nii = (T)(ii - tmpi);
22124                 ir+=(T)tmpr;
22125                 ii+=(T)tmpi;
22126               }
22127               const float nwr = wr*ca-wi*sa;
22128               wi = wi*ca + wr*sa;
22129               wr = nwr;
22130             }
22131           }
22132         }
22133         if (invert) { real/=N; imag/=N; }
22134       } break;
22135       case 'z' : { // Fourier along Z, using built-in functions.
22136         const unsigned int N = real._depth, N2 = (N>>1);
22137         if (((N-1)&N) && N!=1)
22138           throw CImgInstanceException("CImgList<%s>::FFT() : Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Z-axis.",
22139                                       pixel_type(),
22140                                       real._width,real._height,real._depth,real._spectrum);
22141 
22142         for (unsigned int i = 0, j = 0; i<N2; ++i) {
22143           if (j>i) cimg_forXYC(real,x,y,c) {
22144             cimg::swap(real(x,y,i,c),real(x,y,j,c)); cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
22145             if (j<N2) {
22146               const unsigned int ri = N - 1 - i, rj = N - 1 - j;
22147               cimg::swap(real(x,y,ri,c),real(x,y,rj,c)); cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
22148             }
22149           }
22150           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
22151         }
22152         for (unsigned int delta = 2; delta<=N; delta<<=1) {
22153           const unsigned int delta2 = (delta>>1);
22154           for (unsigned int i = 0; i<N; i+=delta) {
22155             float wr = 1, wi = 0;
22156             const float angle = (float)((invert?+1:-1)*2*cimg::PI/delta),
22157                         ca = (float)std::cos(angle), sa = (float)std::sin(angle);
22158             for (unsigned int k = 0; k<delta2; ++k) {
22159               const unsigned int j = i + k, nj = j + delta2;
22160               cimg_forXYC(real,x,y,c) {
22161                 T &ir = real(x,y,j,c), &ii = imag(x,y,j,c), &nir = real(x,y,nj,c), &nii = imag(x,y,nj,c);
22162                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
22163                 nir = (T)(ir - tmpr);
22164                 nii = (T)(ii - tmpi);
22165                 ir+=(T)tmpr;
22166                 ii+=(T)tmpi;
22167               }
22168               const float nwr = wr*ca-wi*sa;
22169               wi = wi*ca + wr*sa;
22170               wr = nwr;
22171             }
22172           }
22173         }
22174         if (invert) { real/=N; imag/=N; }
22175       } break;
22176       default :
22177         throw CImgArgumentException("CImgList<%s>::FFT() : Invalid specified axis '%c' for real and imaginary parts (%u,%u,%u,%u) "
22178                                     "(should be { x | y | z }).",
22179                                     pixel_type(),axis,
22180                                     real._width,real._height,real._depth,real._spectrum);
22181       }
22182 #endif
22183     }
22184 
22185     //! Compute a n-d Fast Fourier Transform.
22186     static void FFT(CImg<T>& real, CImg<T>& imag, const bool invert=false) {
22187       if (!real)
22188         throw CImgInstanceException("CImgList<%s>::FFT() : Empty specified real part.",
22189                                     pixel_type());
22190 
22191       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
22192       if (!real.is_sameXYZC(imag))
22193         throw CImgInstanceException("CImgList<%s>::FFT() : Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
22194                                     pixel_type(),
22195                                     real._width,real._height,real._depth,real._spectrum,real._data,
22196                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
22197 
22198 #ifdef cimg_use_fftw3
22199       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
22200       fftw_plan data_plan;
22201       const unsigned int w = real._width, wh = w*real._height, whd = wh*real._depth;
22202       data_plan = fftw_plan_dft_3d(real._width,real._height,real._depth,data_in,data_in,invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
22203       cimg_forC(real,c) {
22204         T *ptrr = real.data(0,0,0,c), *ptri = imag.data(0,0,0,c);
22205         double *ptrd = (double*)data_in;
22206         for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
22207           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
22208             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
22209               *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri;
22210             }
22211         fftw_execute(data_plan);
22212         ptrd = (double*)data_in;
22213         ptrr = real.data(0,0,0,c);
22214         ptri = imag.data(0,0,0,c);
22215         if (!invert) for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
22216           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
22217             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
22218               *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++);
22219             }
22220         else for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
22221           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
22222             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
22223               *ptrr = (T)(*(ptrd++)/whd); *ptri = (T)(*(ptrd++)/whd);
22224             }
22225       }
22226       fftw_destroy_plan(data_plan);
22227       fftw_free(data_in);
22228 #else
22229       if (real._depth>1)  FFT(real,imag,'z',invert);
22230       if (real._height>1) FFT(real,imag,'y',invert);
22231       if (real._width>1)  FFT(real,imag,'x',invert);
22232 #endif
22233     }
22234 
22235     //@}
22236     //-------------------------------------
22237     //
22238     //! \name 3d Objects Management
22239     //@{
22240     //-------------------------------------
22241 
22242     //! Shift a 3d object.
22243     CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
22244       if (_height!=3 || _depth>1 || _spectrum>1)
22245         throw CImgInstanceException(_cimg_instance
22246                                     "shift_object3d() : Instance is not a set of 3d vertices.",
22247                                     cimg_instance);
22248 
22249       get_shared_line(0)+=tx; get_shared_line(1)+=ty; get_shared_line(2)+=tz;
22250       return *this;
22251     }
22252 
22253     CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
22254       return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
22255     }
22256 
22257     //! Shift a 3d object so that it becomes centered.
22258     CImg<T>& shift_object3d() {
22259       if (_height!=3 || _depth>1 || _spectrum>1)
22260         throw CImgInstanceException(_cimg_instance
22261                                     "shift_object3d() : Instance is not a set of 3d vertices.",
22262                                     cimg_instance);
22263 
22264       CImg<T> xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2);
22265       float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
22266       xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
22267       return *this;
22268     }
22269 
22270     CImg<Tfloat> get_shift_object3d() const {
22271       return CImg<Tfloat>(*this,false).shift_object3d();
22272     }
22273 
22274     //! Resize a 3d object.
22275     CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
22276       if (_height!=3 || _depth>1 || _spectrum>1)
22277         throw CImgInstanceException(_cimg_instance
22278                                     "resize_object3d() : Instance is not a set of 3d vertices.",
22279                                     cimg_instance);
22280 
22281       CImg<T> xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2);
22282       float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
22283       if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
22284       if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
22285       if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
22286       return *this;
22287     }
22288 
22289     CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
22290       return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
22291     }
22292 
22293     //! Resize a 3d object so that its max dimension if one.
22294     CImg<T> resize_object3d() const {
22295       if (_height!=3 || _depth>1 || _spectrum>1)
22296         throw CImgInstanceException(_cimg_instance
22297                                     "resize_object3d() : Instance is not a set of 3d vertices.",
22298                                     cimg_instance);
22299 
22300       CImg<T> xcoords = get_shared_line(0), ycoords = get_shared_line(1), zcoords = get_shared_line(2);
22301       float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
22302       const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
22303       if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
22304       return *this;
22305     }
22306 
22307     CImg<Tfloat> get_resize_object3d() const {
22308       return CImg<Tfloat>(*this,false).resize_object3d();
22309     }
22310 
22311     //! Append a 3d object to another one.
22312     template<typename tf, typename tp, typename tff>
22313     CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices, const CImgList<tff>& obj_primitives) {
22314       if (!obj_vertices || !obj_primitives) return *this;
22315       if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
22316         throw CImgInstanceException(_cimg_instance
22317                                     "append_object3d() : Specified vertice image (%u,%u,%u,%u,%p) is not a set of 3d vertices.",
22318                                     cimg_instance,
22319                                     obj_vertices._width,obj_vertices._height,obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
22320 
22321       if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
22322       if (_height!=3 || _depth>1 || _spectrum>1)
22323         throw CImgInstanceException(_cimg_instance
22324                                     "append_object3d() : Instance is not a set of 3d vertices.",
22325                                     cimg_instance);
22326 
22327       const unsigned int P = _width;
22328       append(obj_vertices,'x');
22329       const unsigned int N = primitives._width;
22330       primitives.insert(obj_primitives);
22331       for (unsigned int i = N; i<primitives._width; ++i) {
22332         CImg<tf> &p = primitives[i];
22333         switch (p.size()) {
22334         case 1 : p[0]+=P; break; // Point.
22335         case 5 : p[0]+=P; p[1]+=P; break; // Sphere.
22336         case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment.
22337         case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle.
22338         case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle.
22339         }
22340       }
22341       return *this;
22342     }
22343 
22344     //! Create and return a 3d elevation of the instance image.
22345     /**
22346        \param[out] primitives The returned list of the 3d object primitives
22347                               (template type \e tf should be at least \e unsigned \e int).
22348        \param[out] colors The returned list of the 3d object colors.
22349        \param elevation The input elevation map.
22350        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
22351        \par Sample code :
22352        \code
22353        const CImg<float> img("reference.jpg");
22354        CImgList<unsigned int> faces3d;
22355        CImgList<unsigned char> colors3d;
22356        const CImg<float> points3d = img.get_elevation3d(faces3d,colors,img.get_norm()*0.2);
22357        CImg<unsigned char>().display_object3d(points3d,faces3d,colors3d);
22358        \endcode
22359        \image html ref_elevation3d.jpg
22360     **/
22361     template<typename tf, typename tc, typename te>
22362     CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
22363       if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
22364         throw CImgArgumentException(_cimg_instance
22365                                     "get_elevation3d() : Instance and specified elevation (%u,%u,%u,%u,%p) "
22366                                     "have incompatible dimensions.",
22367                                     cimg_instance,
22368                                     elevation._width,elevation._height,elevation._depth,elevation._spectrum,elevation._data);
22369 
22370       if (is_empty()) return *this;
22371       float m, M = (float)max_min(m);
22372       if (M==m) ++M;
22373       colors.assign();
22374       const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
22375       for (unsigned int y = 0; y<size_y1; ++y)
22376         for (unsigned int x = 0; x<size_x1; x++) {
22377           const unsigned char
22378             r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
22379             g = _spectrum>1?(unsigned char)(((*this)(x,y,1) - m)*255/(M-m)):r,
22380             b = _spectrum>2?(unsigned char)(((*this)(x,y,2) - m)*255/(M-m)):(_spectrum>1?0:r);
22381           CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
22382         }
22383       const typename CImg<te>::_functor2d_int func(elevation);
22384       return elevation3d(primitives,func,0,0,_width-1.0f,_height-1.0f,_width,_height);
22385     }
22386 
22387     //! Create and return a isoline of the instance image as a 3d object.
22388     /**
22389        \param[out] primitives The returned list of the 3d object primitives
22390                               (template type \e tf should be at least \e unsigned \e int).
22391        \param isovalue The returned list of the 3d object colors.
22392        \param size_x The number of subdivisions along the X-axis.
22393        \param size_y The number of subdisivions along the Y-axis.
22394        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
22395        \par Sample code :
22396        \code
22397        const CImg<float> img("reference.jpg");
22398        CImgList<unsigned int> faces3d;
22399        const CImg<float> points3d = img.get_isoline3d(faces3d,100);
22400        CImg<unsigned char>().display_object3d(points3d,faces3d,colors3d);
22401        \endcode
22402        \image html ref_isoline3d.jpg
22403     **/
22404     template<typename tf>
22405     CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
22406                                const int size_x=-100, const int size_y=-100) const {
22407       if (_spectrum>1)
22408         throw CImgInstanceException(_cimg_instance
22409                                     "get_isoline3d() : Instance is not a scalar image.",
22410                                     cimg_instance);
22411       primitives.assign();
22412       if (is_empty()) return *this;
22413       CImg<floatT> vertices;
22414       if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
22415         const _functor2d_int func(*this);
22416         vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,size_x,size_y);
22417       } else {
22418         const _functor2d_float func(*this);
22419         vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,size_x,size_y);
22420       }
22421       return vertices;
22422     }
22423 
22424     //! Create and return a isosurface of the instance image as a 3d object.
22425     /**
22426        \param[out] primitives The returned list of the 3d object primitives
22427                               (template type \e tf should be at least \e unsigned \e int).
22428        \param isovalue The returned list of the 3d object colors.
22429        \param size_x The number of subdivisions along the X-axis.
22430        \param size_y The number of subdisivions along the Y-axis.
22431        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
22432        \par Sample code :
22433        \code
22434        const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
22435        CImgList<unsigned int> faces3d;
22436        const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
22437        CImg<unsigned char>().display_object3d(points3d,faces3d,colors3d);
22438        \endcode
22439        \image html ref_isosurface3d.jpg
22440     **/
22441     template<typename tf>
22442     CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
22443                                   const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
22444       if (_spectrum>1)
22445         throw CImgInstanceException(_cimg_instance
22446                                     "get_isosurface3d() : Instance is not a scalar image.",
22447                                     cimg_instance);
22448       primitives.assign();
22449       if (is_empty()) return *this;
22450       CImg<floatT> vertices;
22451       if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
22452         const _functor3d_int func(*this);
22453         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,size_x,size_y,size_z);
22454       } else {
22455         const _functor3d_float func(*this);
22456         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,size_x,size_y,size_z);
22457       }
22458       return vertices;
22459     }
22460 
22461     //! Get elevation3d of a function.
22462     template<typename tf, typename tfunc>
22463     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
22464                                     const float x0, const float y0, const float x1, const float y1,
22465                                     const int size_x=256, const int size_y=256) {
22466       const float
22467         nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
22468         nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
22469       const unsigned int
22470         _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100), nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
22471         _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100), nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
22472       if (nsize_x<2 || nsize_y<2)
22473         throw CImgArgumentException("CImg<%s>::elevation3d() : Invalid specified size (%d,%d).",
22474                                     pixel_type(),
22475                                     nsize_x,nsize_y);
22476 
22477       CImg<floatT> vertices(nsize_x*nsize_y,3);
22478       floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
22479       for (unsigned int y = 0; y<nsize_y; ++y) {
22480         const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
22481         for (unsigned int x = 0; x<nsize_x; ++x) {
22482           const float X = nx0 + x*(nx1-nx0)/nsize_x1;
22483           *(ptr_x++) = (float)x;
22484           *(ptr_y++) = (float)y;
22485           *(ptr_z++) = (float)func(X,Y);
22486         }
22487       }
22488       primitives.assign(nsize_x1*nsize_y1,1,4);
22489       for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
22490         const unsigned int yw = y*nsize_x;
22491         for (unsigned int x = 0; x<nsize_x1; ++x) {
22492           const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
22493           primitives[p++].fill(xpyw,xpyww,xpyww+1,xpyw+1);
22494         }
22495       }
22496       return vertices;
22497     }
22498 
22499     template<typename tf>
22500     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
22501                                     const float x0, const float y0, const float x1, const float y1,
22502                                     const int sizex=256, const int sizey=256) {
22503       const _functor2d_expr func(expression);
22504       return elevation3d(primitives,func,x0,y0,x1,y1,sizex,sizey);
22505     }
22506 
22507     //! Get isoline as a 3d object.
22508     template<typename tf, typename tfunc>
22509     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
22510                                   const float x0, const float y0, const float x1, const float y1,
22511                                   const int sizex=256, const int sizey=256) {
22512       static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
22513       static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
22514                                            { 1,2,-1,-1 },   { 0,1,2,3 },   { 0,2,-1,-1 }, { 2,3,-1,-1 },
22515                                            { 2,3,-1,-1 },   { 0,2,-1,-1},  { 0,3,1,2 },   { 1,2,-1,-1 },
22516                                            { 1,3,-1,-1 },   { 0,1,-1,-1},  { 0,3,-1,-1},  { -1,-1,-1,-1 } };
22517       const unsigned int
22518         _nx = (unsigned int)(sizex>=0?sizex:((x1-x0)*-sizex/100 + 1)),
22519         _ny = (unsigned int)(sizey>=0?sizey:((y1-y0)*-sizey/100 + 1)),
22520         nx = _nx?_nx:1,
22521         ny = _ny?_ny:1,
22522         nxm1 = nx - 1,
22523         nym1 = ny - 1;
22524       primitives.assign();
22525       if (!nxm1 || !nym1) return CImg<floatT>();
22526       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
22527       CImgList<floatT> vertices;
22528       CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
22529       CImg<floatT> values1(nx), values2(nx);
22530       float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
22531 
22532       // Fill first line with values
22533       cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
22534 
22535       // Run the marching squares algorithm
22536       for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
22537         X = x0; nX = X + dx;
22538         indices2.fill(-1);
22539         for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
22540 
22541           // Determine square configuration
22542           const float
22543             val0 = values1(xi),
22544             val1 = values1(nxi),
22545             val2 = values2(nxi) = (float)func(nX,nY),
22546             val3 = values2(xi) = (float)func(X,nY);
22547           const unsigned int
22548             configuration = (val0<isovalue?1:0)  | (val1<isovalue?2:0)  | (val2<isovalue?4:0)  | (val3<isovalue?8:0),
22549             edge = edges[configuration];
22550 
22551           // Compute intersection vertices
22552           if (edge) {
22553             if ((edge&1) && indices1(xi,0)<0) {
22554               const float Xi = X + (isovalue-val0)*dx/(val1-val0);
22555               indices1(xi,0) = vertices._width;
22556               CImg<floatT>::vector(Xi,Y,0).move_to(vertices);
22557             }
22558             if ((edge&2) && indices1(nxi,1)<0) {
22559               const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
22560               indices1(nxi,1) = vertices._width;
22561               CImg<floatT>::vector(nX,Yi,0).move_to(vertices);
22562             }
22563             if ((edge&4) && indices2(xi,0)<0) {
22564               const float Xi = X + (isovalue-val3)*dx/(val2-val3);
22565               indices2(xi,0) = vertices._width;
22566               CImg<floatT>::vector(Xi,nY,0).move_to(vertices);
22567             }
22568             if ((edge&8) && indices1(xi,1)<0) {
22569               const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
22570               indices1(xi,1) = vertices._width;
22571               CImg<floatT>::vector(X,Yi,0).move_to(vertices);
22572             }
22573 
22574             // Create segments
22575             for (const int *segment = segments[configuration]; *segment!=-1; ) {
22576               const unsigned int p0 = *(segment++), p1 = *(segment++);
22577               const tf
22578                 i0 = (tf)(_isoline3d_indice(p0,indices1,indices2,xi,nxi)),
22579                 i1 = (tf)(_isoline3d_indice(p1,indices1,indices2,xi,nxi));
22580               CImg<tf>::vector(i0,i1).move_to(primitives);
22581             }
22582           }
22583         }
22584         values1.swap(values2);
22585         indices1.swap(indices2);
22586       }
22587       return vertices>'x';
22588     }
22589 
22590     template<typename tf>
22591     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
22592                                   const float x0, const float y0, const float x1, const float y1,
22593                                   const int sizex=256, const int sizey=256) {
22594       const _functor2d_expr func(expression);
22595       return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,sizex,sizey);
22596     }
22597 
22598     template<typename t>
22599     static int _isoline3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
22600                                  const unsigned int x, const unsigned int nx) {
22601       switch (edge) {
22602       case 0 : return (int)indices1(x,0);
22603       case 1 : return (int)indices1(nx,1);
22604       case 2 : return (int)indices2(x,0);
22605       case 3 : return (int)indices1(x,1);
22606       }
22607       return 0;
22608     }
22609 
22610     //! Get isosurface as a 3d object.
22611     template<typename tf, typename tfunc>
22612     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
22613                                      const float x0, const float y0, const float z0,
22614                                      const float x1, const float y1, const float z1,
22615                                      const int size_x=32, const int size_y=32, const int size_z=32) {
22616       static unsigned int edges[256] = {
22617         0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
22618         0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
22619         0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
22620         0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
22621         0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
22622         0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
22623         0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
22624         0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
22625         0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
22626         0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
22627         0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
22628         0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
22629         0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
22630         0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
22631         0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
22632         0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 };
22633 
22634       static int triangles[256][16] = {
22635         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22636         { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22637         { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22638         { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
22639         { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22640         { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
22641         { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
22642         { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22643         { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22644         { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
22645         { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
22646         { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
22647         { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
22648         { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
22649         { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
22650         { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
22651         { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22652         { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
22653         { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
22654         { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
22655         { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
22656         { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
22657         { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
22658         { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
22659         { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
22660         { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22661         { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
22662         { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
22663         { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
22664         { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
22665         { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
22666         { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22667         { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22668         { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
22669         { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
22670         { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
22671         { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
22672         { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
22673         { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
22674         { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
22675         { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
22676         { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
22677         { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
22678         { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
22679         { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
22680         { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
22681         { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
22682         { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
22683         { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
22684         { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
22685         { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
22686         { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
22687         { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
22688         { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
22689         { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
22690         { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22691         { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
22692         { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
22693         { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
22694         { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22695         { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
22696         { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
22697         { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22698         { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22699         { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22700         { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
22701         { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
22702         { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
22703         { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
22704         { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
22705         { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
22706         { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
22707         { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
22708         { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
22709         { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
22710         { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
22711         { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22712         { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
22713         { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
22714         { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22715         { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
22716         { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
22717         { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
22718         { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
22719         { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
22720         { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
22721         { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
22722         { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
22723         { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
22724         { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
22725         { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
22726         { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
22727         { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
22728         { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22729         { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
22730         { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22731         { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
22732         { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
22733         { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
22734         { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
22735         { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
22736         { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
22737         { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
22738         { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22739         { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
22740         { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
22741         { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
22742         { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22743         { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
22744         { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
22745         { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22746         { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22747         { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
22748         { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
22749         { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
22750         { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
22751         { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
22752         { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22753         { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
22754         { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22755         { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
22756         { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22757         { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
22758         { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22759         { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22760         { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22761         { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
22762         { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
22763       };
22764 
22765       const unsigned int
22766         _nx = (unsigned int)(size_x>=0?size_x:((x1-x0)*-size_x/100 + 1)),
22767         _ny = (unsigned int)(size_y>=0?size_y:((y1-y0)*-size_y/100 + 1)),
22768         _nz = (unsigned int)(size_z>=0?size_y:((z1-z0)*-size_z/100 + 1)),
22769         nx = _nx?_nx:1,
22770         ny = _ny?_ny:1,
22771         nz = _nz?_nz:1,
22772         nxm1 = nx - 1,
22773         nym1 = ny - 1,
22774         nzm1 = nz - 1;
22775       primitives.assign();
22776       if (!nxm1 || !nym1 || !nzm1) return CImg<floatT>();
22777       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
22778       CImgList<floatT> vertices;
22779       CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
22780       CImg<floatT> values1(nx,ny), values2(nx,ny);
22781       float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
22782 
22783       // Fill the first plane with function values
22784       Y = y0;
22785       cimg_forY(values1,y) {
22786         X = x0;
22787         cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
22788         Y+=dy;
22789       }
22790 
22791       // Run Marching Cubes algorithm
22792       Z = z0; nZ = Z + dz;
22793       for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
22794         Y = y0; nY = Y + dy;
22795         indices2.fill(-1);
22796         for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
22797           X = x0; nX = X + dx;
22798           for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
22799 
22800             // Determine cube configuration
22801             const float
22802               val0 = values1(xi,yi),
22803               val1 = values1(nxi,yi),
22804               val2 = values1(nxi,nyi),
22805               val3 = values1(xi,nyi),
22806               val4 = values2(xi,yi) = (float)func(X,Y,nZ),
22807               val5 = values2(nxi,yi) = (float)func(nX,Y,nZ),
22808               val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
22809               val7 = values2(xi,nyi) = (float)func(X,nY,nZ);
22810 
22811             const unsigned int configuration =
22812               (val0<isovalue?1:0)  | (val1<isovalue?2:0)  | (val2<isovalue?4:0)  | (val3<isovalue?8:0) |
22813               (val4<isovalue?16:0) | (val5<isovalue?32:0) | (val6<isovalue?64:0) | (val7<isovalue?128:0),
22814               edge = edges[configuration];
22815 
22816             // Compute intersection vertices
22817             if (edge) {
22818               if ((edge&1) && indices1(xi,yi,0)<0) {
22819                 const float Xi = X + (isovalue-val0)*dx/(val1-val0);
22820                 indices1(xi,yi,0) = vertices._width;
22821                 CImg<floatT>::vector(Xi,Y,Z).move_to(vertices);
22822               }
22823               if ((edge&2) && indices1(nxi,yi,1)<0) {
22824                 const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
22825                 indices1(nxi,yi,1) = vertices._width;
22826                 CImg<floatT>::vector(nX,Yi,Z).move_to(vertices);
22827               }
22828               if ((edge&4) && indices1(xi,nyi,0)<0) {
22829                 const float Xi = X + (isovalue-val3)*dx/(val2-val3);
22830                 indices1(xi,nyi,0) = vertices._width;
22831                 CImg<floatT>::vector(Xi,nY,Z).move_to(vertices);
22832               }
22833               if ((edge&8) && indices1(xi,yi,1)<0) {
22834                 const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
22835                 indices1(xi,yi,1) = vertices._width;
22836                 CImg<floatT>::vector(X,Yi,Z).move_to(vertices);
22837               }
22838               if ((edge&16) && indices2(xi,yi,0)<0) {
22839                 const float Xi = X + (isovalue-val4)*dx/(val5-val4);
22840                 indices2(xi,yi,0) = vertices._width;
22841                 CImg<floatT>::vector(Xi,Y,nZ).move_to(vertices);
22842               }
22843               if ((edge&32) && indices2(nxi,yi,1)<0) {
22844                 const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
22845                 indices2(nxi,yi,1) = vertices._width;
22846                 CImg<floatT>::vector(nX,Yi,nZ).move_to(vertices);
22847               }
22848               if ((edge&64) && indices2(xi,nyi,0)<0) {
22849                 const float Xi = X + (isovalue-val7)*dx/(val6-val7);
22850                 indices2(xi,nyi,0) = vertices._width;
22851                 CImg<floatT>::vector(Xi,nY,nZ).move_to(vertices);
22852               }
22853               if ((edge&128) && indices2(xi,yi,1)<0)  {
22854                 const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
22855                 indices2(xi,yi,1) = vertices._width;
22856                 CImg<floatT>::vector(X,Yi,nZ).move_to(vertices);
22857               }
22858               if ((edge&256) && indices1(xi,yi,2)<0) {
22859                 const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
22860                 indices1(xi,yi,2) = vertices._width;
22861                 CImg<floatT>::vector(X,Y,Zi).move_to(vertices);
22862               }
22863               if ((edge&512) && indices1(nxi,yi,2)<0)  {
22864                 const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
22865                 indices1(nxi,yi,2) = vertices._width;
22866                 CImg<floatT>::vector(nX,Y,Zi).move_to(vertices);
22867               }
22868               if ((edge&1024) && indices1(nxi,nyi,2)<0) {
22869                 const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
22870                 indices1(nxi,nyi,2) = vertices._width;
22871                 CImg<floatT>::vector(nX,nY,Zi).move_to(vertices);
22872               }
22873               if ((edge&2048) && indices1(xi,nyi,2)<0) {
22874                 const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
22875                 indices1(xi,nyi,2) = vertices._width;
22876                 CImg<floatT>::vector(X,nY,Zi).move_to(vertices);
22877               }
22878 
22879               // Create triangles
22880               for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
22881                 const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++);
22882                 const tf
22883                   i0 = (tf)(_isosurface3d_indice(p0,indices1,indices2,xi,yi,nxi,nyi)),
22884                   i1 = (tf)(_isosurface3d_indice(p1,indices1,indices2,xi,yi,nxi,nyi)),
22885                   i2 = (tf)(_isosurface3d_indice(p2,indices1,indices2,xi,yi,nxi,nyi));
22886                 CImg<tf>::vector(i0,i2,i1).move_to(primitives);
22887               }
22888             }
22889           }
22890         }
22891         cimg::swap(values1,values2);
22892         cimg::swap(indices1,indices2);
22893       }
22894       return vertices>'x';
22895     }
22896 
22897     template<typename tf>
22898     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
22899                                      const float x0, const float y0, const float z0,
22900                                      const float x1, const float y1, const float z1,
22901                                      const int dx=32, const int dy=32, const int dz=32) {
22902       const _functor3d_expr func(expression);
22903       return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
22904     }
22905 
22906     template<typename t>
22907     static int _isosurface3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
22908                                     const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) {
22909       switch (edge) {
22910       case 0 : return indices1(x,y,0);
22911       case 1 : return indices1(nx,y,1);
22912       case 2 : return indices1(x,ny,0);
22913       case 3 : return indices1(x,y,1);
22914       case 4 : return indices2(x,y,0);
22915       case 5 : return indices2(nx,y,1);
22916       case 6 : return indices2(x,ny,0);
22917       case 7 : return indices2(x,y,1);
22918       case 8 : return indices1(x,y,2);
22919       case 9 : return indices1(nx,y,2);
22920       case 10 : return indices1(nx,ny,2);
22921       case 11 : return indices1(x,ny,2);
22922       }
22923       return 0;
22924     }
22925 
22926     // Define functors for accessing image values (used in previous functions).
22927     struct _functor2d_int {
22928       const CImg<T>& ref;
22929       _functor2d_int(const CImg<T>& pref):ref(pref) {}
22930       float operator()(const float x, const float y) const {
22931         return (float)ref((int)x,(int)y);
22932       }
22933     };
22934 
22935     struct _functor2d_float {
22936       const CImg<T>& ref;
22937       _functor2d_float(const CImg<T>& pref):ref(pref) {}
22938       float operator()(const float x, const float y) const {
22939         return (float)ref._linear_atXY(x,y);
22940       }
22941     };
22942 
22943     struct _functor2d_expr {
22944       _cimg_math_parser *mp;
22945       _functor2d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
22946       ~_functor2d_expr() { if (mp) delete mp; }
22947       float operator()(const float x, const float y) const {
22948         return (float)mp->eval(x,y,0,0);
22949       }
22950     };
22951 
22952     struct _functor3d_int {
22953       const CImg<T>& ref;
22954       _functor3d_int(const CImg<T>& pref):ref(pref) {}
22955       float operator()(const float x, const float y, const float z) const {
22956         return (float)ref((int)x,(int)y,(int)z);
22957       }
22958     };
22959 
22960     struct _functor3d_float {
22961       const CImg<T>& ref;
22962       _functor3d_float(const CImg<T>& pref):ref(pref) {}
22963       float operator()(const float x, const float y, const float z) const {
22964         return (float)ref._linear_atXYZ(x,y,z);
22965       }
22966     };
22967 
22968     struct _functor3d_expr {
22969       _cimg_math_parser *mp;
22970       ~_functor3d_expr() { if (mp) delete mp; }
22971       _functor3d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
22972       float operator()(const float x, const float y, const float z) const {
22973         return (float)mp->eval(x,y,z,0);
22974       }
22975     };
22976 
22977     struct _functor4d_int {
22978       const CImg<T>& ref;
22979       _functor4d_int(const CImg<T>& pref):ref(pref) {}
22980       float operator()(const float x, const float y, const float z, const unsigned int c) const {
22981         return (float)ref((int)x,(int)y,(int)z,c);
22982       }
22983     };
22984 
22985     //! Create and return a 3d box object.
22986     /**
22987        \param[out] primitives The returned list of the 3d object primitives
22988                               (template type \e tf should be at least \e unsigned \e int).
22989        \param size_x The width of the box (dimension along the X-axis).
22990        \param size_y The height of the box (dimension along the Y-axis).
22991        \param size_z The depth of the box (dimension along the Z-axis).
22992        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
22993        \par Sample code :
22994        \code
22995        CImgList<unsigned int> faces3d;
22996        const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
22997        CImg<unsigned char>().display_object3d(points3d,faces3d);
22998        \endcode
22999        \image html ref_box3d.jpg
23000     **/
23001     template<typename tf>
23002     static CImg<floatT> box3d(CImgList<tf>& primitives,
23003                               const float size_x=200, const float size_y=100, const float size_z=100) {
23004       primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5);
23005       return CImg<floatT>(8,3,1,1,
23006                           0.,size_x,size_x,    0.,    0.,size_x,size_x,    0.,
23007                           0.,    0.,size_y,size_y,    0.,    0.,size_y,size_y,
23008                           0.,    0.,    0.,    0.,size_z,size_z,size_z,size_z);
23009     }
23010 
23011     //! Create and return a 3d cone.
23012     /**
23013        \param[out] primitives The returned list of the 3d object primitives
23014                               (template type \e tf should be at least \e unsigned \e int).
23015        \param radius The radius of the cone basis.
23016        \param size_z The cone's height.
23017        \param subdivisions The number of basis angular subdivisions.
23018        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
23019        \par Sample code :
23020        \code
23021        CImgList<unsigned int> faces3d;
23022        const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
23023        CImg<unsigned char>().display_object3d(points3d,faces3d);
23024        \endcode
23025        \image html ref_cone3d.jpg
23026     **/
23027     template<typename tf>
23028     static CImg<floatT> cone3d(CImgList<tf>& primitives,
23029                                const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
23030       primitives.assign();
23031       if (!subdivisions) return CImg<floatT>();
23032       CImgList<floatT> vertices(2,1,3,1,1,
23033                                 0.,0.,size_z,
23034                                 0.,0.,0.);
23035       for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
23036         const float a = (float)(angle*cimg::PI/180);
23037         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
23038       }
23039       const unsigned int nbr = vertices._width - 2;
23040       for (unsigned int p = 0; p<nbr; ++p) {
23041         const unsigned int curr = 2 + p, next = 2 + ((p+1)%nbr);
23042         CImg<tf>::vector(1,next,curr).move_to(primitives);
23043         CImg<tf>::vector(0,curr,next).move_to(primitives);
23044       }
23045       return vertices>'x';
23046     }
23047 
23048     //! Create and return a 3d cylinder.
23049     /**
23050        \param[out] primitives The returned list of the 3d object primitives
23051                               (template type \e tf should be at least \e unsigned \e int).
23052        \param radius The radius of the cylinder basis.
23053        \param size_z The cylinder's height.
23054        \param subdivisions The number of basis angular subdivisions.
23055        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
23056        \par Sample code :
23057        \code
23058        CImgList<unsigned int> faces3d;
23059        const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
23060        CImg<unsigned char>().display_object3d(points3d,faces3d);
23061        \endcode
23062        \image html ref_cylinder3d.jpg
23063     **/
23064     template<typename tf>
23065     static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
23066                                    const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
23067       primitives.assign();
23068       if (!subdivisions) return CImg<floatT>();
23069       CImgList<floatT> vertices(2,1,3,1,1,
23070                                 0.,0.,0.,
23071                                 0.,0.,size_z);
23072       for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
23073         const float a = (float)(angle*cimg::PI/180);
23074         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.0f).move_to(vertices);
23075         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
23076       }
23077       const unsigned int nbr = (vertices._width - 2)/2;
23078       for (unsigned int p = 0; p<nbr; ++p) {
23079         const unsigned int curr = 2+2*p, next = 2+(2*((p+1)%nbr));
23080         CImg<tf>::vector(0,next,curr).move_to(primitives);
23081         CImg<tf>::vector(1,curr+1,next+1).move_to(primitives);
23082         CImg<tf>::vector(curr,next,next+1,curr+1).move_to(primitives);
23083       }
23084       return vertices>'x';
23085     }
23086 
23087     //! Create and return a 3d torus.
23088     /**
23089        \param[out] primitives The returned list of the 3d object primitives
23090                               (template type \e tf should be at least \e unsigned \e int).
23091        \param radius1 The large radius.
23092        \param radius2 The small radius.
23093        \param subdivisions1 The number of angular subdivisions for the large radius.
23094        \param subdivisions2 The number of angular subdivisions for the small radius.
23095        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
23096        \par Sample code :
23097        \code
23098        CImgList<unsigned int> faces3d;
23099        const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
23100        CImg<unsigned char>().display_object3d(points3d,faces3d);
23101        \endcode
23102        \image html ref_torus3d.jpg
23103     **/
23104     template<typename tf>
23105     static CImg<floatT> torus3d(CImgList<tf>& primitives,
23106                                 const float radius1=100, const float radius2=30,
23107                                 const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
23108       primitives.assign();
23109       if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
23110       CImgList<floatT> vertices;
23111       for (unsigned int v = 0; v<subdivisions1; ++v) {
23112         const float
23113           beta = (float)(v*2*cimg::PI/subdivisions1),
23114           xc = radius1*(float)std::cos(beta),
23115           yc = radius1*(float)std::sin(beta);
23116         for (unsigned int u = 0; u<subdivisions2; ++u) {
23117           const float
23118             alpha = (float)(u*2*cimg::PI/subdivisions2),
23119             x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
23120             y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
23121             z = radius2*(float)std::sin(alpha);
23122           CImg<floatT>::vector(x,y,z).move_to(vertices);
23123         }
23124       }
23125       for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
23126         const unsigned int nv = (vv+1)%subdivisions1;
23127         for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
23128           const unsigned int nu = (uu+1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
23129           CImg<tf>::vector(svv+nu,svv+uu,snv+uu).move_to(primitives);
23130           CImg<tf>::vector(svv+nu,snv+uu,snv+nu).move_to(primitives);
23131         }
23132       }
23133       return vertices>'x';
23134     }
23135 
23136     //! Create and return a 3d XY-plane.
23137     /**
23138        \param[out] primitives The returned list of the 3d object primitives
23139                               (template type \e tf should be at least \e unsigned \e int).
23140        \param size_x The width of the plane (dimension along the X-axis).
23141        \param size_y The height of the plane (dimensions along the Y-axis).
23142        \param subdivisions_x The number of planar subdivisions along the X-axis.
23143        \param subdivisions_y The number of planar subdivisions along the Y-axis.
23144        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
23145        \par Sample code :
23146        \code
23147        CImgList<unsigned int> faces3d;
23148        const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
23149        CImg<unsigned char>().display_object3d(points3d,faces3d);
23150        \endcode
23151        \image html ref_plane3d.jpg
23152     **/
23153     template<typename tf>
23154     static CImg<floatT> plane3d(CImgList<tf>& primitives,
23155                                 const float size_x=100, const float size_y=100,
23156                                 const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
23157       primitives.assign();
23158       if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
23159       CImgList<floatT> vertices;
23160       const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
23161       const float fx = (float)size_x/w, fy = (float)size_y/h;
23162       for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
23163         CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
23164       for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
23165         const int off1 = x+y*w, off2 = x+1+y*w, off3 = x+1+(y+1)*w, off4 = x+(y+1)*w;
23166         CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
23167       }
23168       return vertices>'x';
23169     }
23170 
23171     //! Create and return a 3d sphere.
23172     /**
23173        \param[out] primitives The returned list of the 3d object primitives
23174                               (template type \e tf should be at least \e unsigned \e int).
23175        \param radius The radius of the sphere (dimension along the X-axis).
23176        \param subdivisions The number of recursive subdivisions from an initial icosahedron.
23177        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
23178        \par Sample code :
23179        \code
23180        CImgList<unsigned int> faces3d;
23181        const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
23182        CImg<unsigned char>().display_object3d(points3d,faces3d);
23183        \endcode
23184        \image html ref_sphere3d.jpg
23185     **/
23186     template<typename tf>
23187     static CImg<floatT> sphere3d(CImgList<tf>& primitives,
23188                                  const float radius=50, const unsigned int subdivisions=3) {
23189 
23190       // Create initial icosahedron
23191       primitives.assign();
23192       const double tmp = (1+std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1+tmp*tmp), b = tmp*a;
23193       CImgList<floatT> vertices(12,1,3,1,1, b,a,0.0, -b,a,0.0, -b,-a,0.0, b,-a,0.0, a,0.0,b, a,0.0,-b,
23194                                 -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.0,b,-a);
23195       primitives.assign(20,1,3,1,1, 4,8,7, 4,7,9, 5,6,11, 5,10,6, 0,4,3, 0,3,5, 2,7,1, 2,1,6,
23196                         8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
23197                         5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
23198       // edge - length/2
23199       float he = (float)a;
23200 
23201       // Recurse subdivisions
23202       for (unsigned int i = 0; i<subdivisions; ++i) {
23203         const unsigned int L = primitives._width;
23204         he/=2;
23205         const float he2 = he*he;
23206         for (unsigned int l = 0; l<L; ++l) {
23207           const unsigned int
23208             p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
23209           const float
23210             x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
23211             x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
23212             x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
23213             tnx0 = (x0+x1)/2, tny0 = (y0+y1)/2, tnz0 = (z0+z1)/2, nn0 = (float)std::sqrt(tnx0*tnx0+tny0*tny0+tnz0*tnz0),
23214             tnx1 = (x0+x2)/2, tny1 = (y0+y2)/2, tnz1 = (z0+z2)/2, nn1 = (float)std::sqrt(tnx1*tnx1+tny1*tny1+tnz1*tnz1),
23215             tnx2 = (x1+x2)/2, tny2 = (y1+y2)/2, tnz2 = (z1+z2)/2, nn2 = (float)std::sqrt(tnx2*tnx2+tny2*tny2+tnz2*tnz2),
23216             nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
23217             nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
23218             nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
23219           int i0 = -1, i1 = -1, i2 = -1;
23220           cimglist_for(vertices,p) {
23221             const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
23222             if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
23223             if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
23224             if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
23225           }
23226           if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices._width - 1; }
23227           if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices._width - 1; }
23228           if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices._width - 1; }
23229           primitives.remove(0);
23230           CImg<tf>::vector(p0,i0,i1).move_to(primitives);
23231           CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
23232           CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
23233           CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
23234         }
23235       }
23236       return (vertices>'x')*=radius;
23237     }
23238 
23239     //! Create and return a 3d ellipsoid.
23240     /**
23241        \param[out] primitives The returned list of the 3d object primitives
23242                               (template type \e tf should be at least \e unsigned \e int).
23243        \param tensor The tensor which gives the shape and size of the ellipsoid.
23244        \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
23245        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N-1).
23246        \par Sample code :
23247        \code
23248        CImgList<unsigned int> faces3d;
23249        const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
23250                          points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
23251        CImg<unsigned char>().display_object3d(points3d,faces3d);
23252        \endcode
23253        \image html ref_ellipsoid3d.jpg
23254     **/
23255     template<typename tf, typename t>
23256     static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
23257                                     const CImg<t>& tensor, const unsigned int subdivisions=3) {
23258       primitives.assign();
23259       if (!subdivisions) return CImg<floatT>();
23260       CImg<floatT> S, V;
23261       tensor.symmetric_eigen(S,V);
23262       const float l0 = S[0], l1 = S[1], l2 = S[2];
23263       CImg<floatT> vertices = sphere(primitives,subdivisions);
23264       vertices.get_shared_line(0)*=l0;
23265       vertices.get_shared_line(1)*=l1;
23266       vertices.get_shared_line(2)*=l2;
23267       return V.transpose()*vertices;
23268     }
23269 
23270     //! Convert a 3d object into a CImg3d.
23271     template<typename tp, typename tc, typename to>
23272     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
23273                               const CImgList<tc>& colors,
23274                               const to& opacities) {
23275       return get_object3dtoCImg3d(primitives,colors,opacities).move_to(*this);
23276     }
23277 
23278     template<typename tp, typename tc>
23279     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
23280                               const CImgList<tc>& colors) {
23281       return get_object3dtoCImg3d(primitives,colors).move_to(*this);
23282     }
23283 
23284     template<typename tp>
23285     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives) {
23286       return get_object3dtoCImg3d(primitives).move_to(*this);
23287     }
23288 
23289     CImg<T>& object3dtoCImg3d() {
23290       return get_object3dtoCImg3d().move_to(*this);
23291     }
23292 
23293     template<typename tp, typename tc, typename to>
23294     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
23295                                       const CImgList<tc>& colors,
23296                                       const to& opacities) const {
23297       char error_message[1024] = { 0 };
23298       if (!is_object3d(primitives,colors,opacities,true,error_message))
23299         throw CImgInstanceException(_cimg_instance
23300                                     "object3dtoCImg3d() : Invalid specified 3d object (%u,%u) (%s).",
23301                                     cimg_instance,_width,primitives._width,error_message);
23302       CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
23303       float *ptrd = res._data;
23304 
23305       // Put magick number.
23306       *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
23307       *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
23308 
23309       // Put number of vertices and primitives.
23310       *(ptrd++) = (float)_width;
23311       *(ptrd++) = (float)primitives._width;
23312 
23313       // Put vertex data.
23314       if (is_empty() || !primitives) return res;
23315       const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
23316       cimg_forX(*this,p) { *(ptrd++) = (float)*(ptrx++); *(ptrd++) = (float)*(ptry++); *(ptrd++) = (float)*(ptrz++); }
23317 
23318       // Put primitive data.
23319       cimglist_for(primitives,p) {
23320         *(ptrd++) = (float)primitives[p].size();
23321         const tp *ptrp = primitives[p]._data;
23322         cimg_foroff(primitives[p],i) *(ptrd++) = (float)*(ptrp++);
23323       }
23324 
23325       // Put color/texture data.
23326       const unsigned int csiz = cimg::min(colors._width,primitives._width);
23327       for (int c = 0; c<(int)csiz; ++c) {
23328         const CImg<tc>& color = colors[c];
23329         const tc *ptrc = color._data;
23330         if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
23331         else {
23332           *(ptrd++) = -128.0f;
23333           int shared_ind = -1;
23334           if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
23335           if (shared_ind<0) {
23336             *(ptrd++) = (float)color._width;
23337             *(ptrd++) = (float)color._height;
23338             *(ptrd++) = (float)color._spectrum;
23339             cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
23340           } else {
23341             *(ptrd++) = (float)shared_ind;
23342             *(ptrd++) = 0;
23343             *(ptrd++) = 0;
23344           }
23345         }
23346       }
23347       const int csiz2 = primitives._width - colors._width;
23348       for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; }
23349 
23350       // Put opacity data.
23351       ptrd = _object3dtoCImg3d(opacities,ptrd);
23352       const unsigned int osiz = primitives._width - opacities._width;
23353       for (unsigned int o = 0; o<osiz; ++o) *(ptrd++) = 1.0f;
23354       return res;
23355     }
23356 
23357     template<typename to>
23358     float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
23359       cimglist_for(opacities,o) {
23360         const CImg<to>& opacity = opacities[o];
23361         const to *ptro = opacity._data;
23362         if (opacity.size()==1) *(ptrd++) = (float)*ptro;
23363         else {
23364           *(ptrd++) = -128.0f;
23365           int shared_ind = -1;
23366           if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
23367           if (shared_ind<0) {
23368             *(ptrd++) = (float)opacity._width;
23369             *(ptrd++) = (float)opacity._height;
23370             *(ptrd++) = (float)opacity._spectrum;
23371             cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
23372           } else {
23373             *(ptrd++) = (float)shared_ind;
23374             *(ptrd++) = 0;
23375             *(ptrd++) = 0;
23376           }
23377         }
23378       }
23379       return ptrd;
23380     }
23381 
23382     template<typename to>
23383     float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
23384       const to *ptro = opacities._data;
23385       cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
23386       return ptrd;
23387     }
23388 
23389     template<typename tp, typename tc, typename to>
23390     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
23391                                         const CImgList<tc>& colors,
23392                                         const CImgList<to>& opacities) const {
23393       unsigned int siz = 8 + 3*width();
23394       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
23395       for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
23396         if (colors[c].is_shared()) siz+=4;
23397         else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3; }
23398       }
23399       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
23400       cimglist_for(opacities,o) {
23401         if (opacities[o].is_shared()) siz+=4;
23402         else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4+osiz:1; }
23403       }
23404       siz+=primitives._width - opacities._width;
23405       return siz;
23406     }
23407 
23408     template<typename tp, typename tc, typename to>
23409     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
23410                                         const CImgList<tc>& colors,
23411                                         const CImg<to>& opacities) const {
23412       unsigned int siz = 8 + 3*width();
23413       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
23414       for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
23415         const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3;
23416       }
23417       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
23418       siz+=opacities.size();
23419       return siz;
23420     }
23421 
23422     template<typename tp, typename tc>
23423     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
23424                                       const CImgList<tc>& colors) const {
23425       CImgList<T> opacities;
23426       return get_object3dtoCImg3d(primitives,colors,opacities);
23427     }
23428 
23429     template<typename tp>
23430     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives) const {
23431       CImgList<T> colors, opacities;
23432       return get_object3dtoCImg3d(primitives,colors,opacities);
23433     }
23434 
23435     CImg<floatT> get_object3dtoCImg3d() const {
23436       CImgList<T> opacities, colors;
23437       CImgList<uintT> primitives(width(),1,1,1,1);
23438       cimglist_for(primitives,p) primitives(p,0) = p;
23439       return get_object3dtoCImg3d(primitives,colors,opacities);
23440     }
23441 
23442     //! Convert a CImg3d (one-column image) into a 3d object.
23443     template<typename tp, typename tc, typename to>
23444     CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImgList<to>& opacities) const {
23445       char error_message[1024] = { 0 };
23446       if (!is_CImg3d(true,error_message))
23447         throw CImgInstanceException(_cimg_instance
23448                                     "CImg3dtoobject3d() : Instance image is not a CImg3d (%s).",
23449                                     cimg_instance,error_message);
23450       const T *ptrs = _data + 6;
23451       const unsigned int nb_points = (unsigned int)*(ptrs++), nb_primitives = (unsigned int)*(ptrs++);
23452       const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
23453       ptrs+=3*nb_points;
23454       primitives.assign(nb_primitives);
23455       cimglist_for(primitives,p) {
23456         const unsigned int nb_inds = (unsigned int)*(ptrs++);
23457         primitives[p].assign(ptrs,1,nb_inds,1,1,false);
23458         ptrs+=nb_inds;
23459       }
23460       colors.assign(nb_primitives);
23461       cimglist_for(colors,c) {
23462         if ((int)*ptrs==-128) {
23463           ++ptrs;
23464           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
23465           if (!h && !s) colors[c].assign(colors[w],true);
23466           else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
23467         } else { colors[c].assign(ptrs,1,3,1,1,false); ptrs+=3; }
23468       }
23469       opacities.assign(nb_primitives);
23470       cimglist_for(opacities,o) {
23471         if ((int)*ptrs==-128) {
23472           ++ptrs;
23473           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
23474           if (!h && !s) opacities[o].assign(opacities[w],true);
23475           else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
23476         } else opacities[o].assign(1,1,1,1,*(ptrs++));
23477       }
23478       return points;
23479     }
23480 
23481     template<typename tp, typename tc, typename to>
23482     CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImgList<to>& opacities) {
23483       return get_CImg3dtoobject3d(primitives,colors,opacities).move_to(*this);
23484     }
23485 
23486     //@}
23487     //---------------------------
23488     //
23489     //! \name Drawing Functions
23490     //@{
23491     //---------------------------
23492 
23493     // The following _draw_scanline() routines are *non user-friendly functions*, used only for internal purpose.
23494     // Pre-requisites : x0<x1, y-coordinate is valid, col is valid.
23495     template<typename tc>
23496     CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
23497                             const tc *const color, const float opacity=1,
23498                             const float brightness=1, const bool init=false) {
23499       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
23500       static float nopacity = 0, copacity = 0;
23501       static unsigned int whd = 0;
23502       static const tc *col = 0;
23503       if (init) {
23504         nopacity = cimg::abs(opacity);
23505         copacity = 1 - cimg::max(opacity,0);
23506         whd = _width*_height*_depth;
23507       } else {
23508         const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width()-1, dx = nx1 - nx0;
23509         if (dx>=0) {
23510           col = color;
23511           const unsigned int off = whd-dx-1;
23512           T *ptrd = data(nx0,y);
23513           if (opacity>=1) { // ** Opaque drawing **
23514             if (brightness==1) { // Brightness==1
23515               if (sizeof(T)!=1) cimg_forC(*this,c) {
23516                 const T val = (T)*(col++);
23517                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
23518                 ptrd+=off;
23519               } else cimg_forC(*this,c) {
23520                 const T val = (T)*(col++);
23521                 std::memset(ptrd,(int)val,dx+1);
23522                 ptrd+=whd;
23523               }
23524             } else if (brightness<1) { // Brightness<1
23525               if (sizeof(T)!=1) cimg_forC(*this,c) {
23526                 const T val = (T)(*(col++)*brightness);
23527                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
23528                 ptrd+=off;
23529               } else cimg_forC(*this,c) {
23530                 const T val = (T)(*(col++)*brightness);
23531                 std::memset(ptrd,(int)val,dx+1);
23532                 ptrd+=whd;
23533               }
23534             } else { // Brightness>1
23535               if (sizeof(T)!=1) cimg_forC(*this,c) {
23536                 const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
23537                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
23538                 ptrd+=off;
23539               } else cimg_forC(*this,c) {
23540                 const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
23541                 std::memset(ptrd,(int)val,dx+1);
23542                 ptrd+=whd;
23543               }
23544             }
23545           } else { // ** Transparent drawing **
23546             if (brightness==1) { // Brightness==1
23547               cimg_forC(*this,c) {
23548                 const T val = (T)*(col++);
23549                 for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
23550                 ptrd+=off;
23551               }
23552             } else if (brightness<=1) { // Brightness<1
23553               cimg_forC(*this,c) {
23554                 const T val = (T)(*(col++)*brightness);
23555                 for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
23556                 ptrd+=off;
23557               }
23558             } else { // Brightness>1
23559               cimg_forC(*this,c) {
23560                 const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
23561                 for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
23562                 ptrd+=off;
23563               }
23564             }
23565           }
23566         }
23567       }
23568       return *this;
23569     }
23570 
23571     template<typename tc>
23572     CImg<T>& _draw_scanline(const tc *const color, const float opacity=1) {
23573       return _draw_scanline(0,0,0,color,opacity,0,true);
23574     }
23575 
23576     //! Draw a 2d colored point (pixel).
23577     /**
23578        \param x0 X-coordinate of the point.
23579        \param y0 Y-coordinate of the point.
23580        \param color Pointer to \c spectrum() consecutive values, defining the color values.
23581        \param opacity Drawing opacity (optional).
23582        \note
23583        - Clipping is supported.
23584        - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
23585        \par Example:
23586        \code
23587        CImg<unsigned char> img(100,100,1,3,0);
23588        const unsigned char color[] = { 255,128,64 };
23589        img.draw_point(50,50,color);
23590        \endcode
23591     **/
23592     template<typename tc>
23593     CImg<T>& draw_point(const int x0, const int y0,
23594                         const tc *const color, const float opacity=1) {
23595       return draw_point(x0,y0,0,color,opacity);
23596     }
23597 
23598     //! Draw a 3d colored point (voxel).
23599     template<typename tc>
23600     CImg<T>& draw_point(const int x0, const int y0, const int z0,
23601                         const tc *const color, const float opacity=1) {
23602       if (!color)
23603         throw CImgArgumentException(_cimg_instance
23604                                     "draw_point() : Specified color is (null).",
23605                                     cimg_instance);
23606 
23607       if (is_empty()) return *this;
23608       if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
23609         const unsigned int whd = _width*_height*_depth;
23610         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
23611         T *ptrd = data(x0,y0,z0,0);
23612         const tc *col = color;
23613         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
23614         else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
23615       }
23616       return *this;
23617     }
23618 
23619     // Draw a cloud of colored points.
23620     template<typename t, typename tc>
23621     CImg<T>& draw_point(const CImg<t>& points,
23622                         const tc *const color, const float opacity=1) {
23623       if (is_empty() || !points) return *this;
23624       switch (points._height) {
23625       case 0 : case 1 :
23626         throw CImgArgumentException(_cimg_instance
23627                                     "draw_point() : Invalid specified point set (%u,%u,%u,%u,%p).",
23628                                     cimg_instance,
23629                                     points._width,points._height,points._depth,points._spectrum,points._data);
23630       case 2 : {
23631         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
23632       } break;
23633       default : {
23634         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
23635       }
23636       }
23637       return *this;
23638     }
23639 
23640     //! Draw a 2d colored line.
23641     /**
23642        \param x0 X-coordinate of the starting line point.
23643        \param y0 Y-coordinate of the starting line point.
23644        \param x1 X-coordinate of the ending line point.
23645        \param y1 Y-coordinate of the ending line point.
23646        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
23647        \param opacity Drawing opacity (optional).
23648        \param pattern An integer whose bits describe the line pattern (optional).
23649        \param init_hatch Flag telling if a reinitialization of the hash state must be done (optional).
23650        \note
23651        - Clipping is supported.
23652        - Line routine uses Bresenham's algorithm.
23653        - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
23654        \par Example:
23655        \code
23656        CImg<unsigned char> img(100,100,1,3,0);
23657        const unsigned char color[] = { 255,128,64 };
23658         img.draw_line(40,40,80,70,color);
23659        \endcode
23660     **/
23661     template<typename tc>
23662     CImg<T>& draw_line(const int x0, const int y0,
23663                        const int x1, const int y1,
23664                        const tc *const color, const float opacity=1,
23665                        const unsigned int pattern=~0U, const bool init_hatch=true) {
23666       if (!color)
23667         throw CImgArgumentException(_cimg_instance
23668                                     "draw_line() : Specified color is (null).",
23669                                     cimg_instance);
23670 
23671       if (is_empty()) return *this;
23672       static unsigned int hatch = ~0U - (~0U>>1);
23673       if (init_hatch) hatch = ~0U - (~0U>>1);
23674       const bool xdir = x0<x1, ydir = y0<y1;
23675       int
23676         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
23677         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
23678         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
23679         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
23680         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
23681       if (xright<0 || xleft>=width()) return *this;
23682       if (xleft<0) { yleft-=xleft*(yright - yleft)/(xright - xleft); xleft = 0; }
23683       if (xright>=width()) { yright-=(xright - width())*(yright - yleft)/(xright - xleft); xright = width()-1; }
23684       if (ydown<0 || yup>=height()) return *this;
23685       if (yup<0) { xup-=yup*(xdown - xup)/(ydown - yup); yup = 0; }
23686       if (ydown>=height()) { xdown-=(ydown - height())*(xdown - xup)/(ydown - yup); ydown = height()-1; }
23687       T *ptrd0 = data(nx0,ny0);
23688       int dx = xright - xleft, dy = ydown - yup;
23689       const bool steep = dy>dx;
23690       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
23691       const int
23692         offx = (nx0<nx1?1:-1)*(steep?_width:1),
23693         offy = (ny0<ny1?1:-1)*(steep?1:_width),
23694         wh = _width*_height;
23695       if (opacity>=1) {
23696         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
23697           if (pattern&hatch) {
23698             T *ptrd = ptrd0; const tc* col = color;
23699             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
23700           }
23701           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
23702           ptrd0+=offx;
23703           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
23704         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
23705           T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
23706           ptrd0+=offx;
23707           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
23708         }
23709       } else {
23710         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
23711         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
23712           if (pattern&hatch) {
23713             T *ptrd = ptrd0; const tc* col = color;
23714             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
23715           }
23716           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
23717           ptrd0+=offx;
23718           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
23719         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
23720           T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
23721           ptrd0+=offx;
23722           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
23723         }
23724       }
23725       return *this;
23726     }
23727 
23728     //! Draw a 2d colored line, with z-buffering.
23729     template<typename tc>
23730     CImg<T>& draw_line(CImg<floatT>& zbuffer,
23731                        const int x0, const int y0, const float z0,
23732                        const int x1, const int y1, const float z1,
23733                        const tc *const color, const float opacity=1,
23734                        const unsigned int pattern=~0U, const bool init_hatch=true) {
23735       if (!color)
23736         throw CImgArgumentException(_cimg_instance
23737                                     "draw_line() : Specified color is (null).",
23738                                     cimg_instance);
23739       if (!is_sameXY(zbuffer))
23740         throw CImgArgumentException(_cimg_instance
23741                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
23742                                     cimg_instance,
23743                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
23744 
23745       if (is_empty() || z0<=0 || z1<=0) return *this;
23746       static unsigned int hatch = ~0U - (~0U>>1);
23747       if (init_hatch) hatch = ~0U - (~0U>>1);
23748       const bool xdir = x0<x1, ydir = y0<y1;
23749       int
23750         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
23751         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
23752         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
23753         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
23754         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
23755       float
23756         Z0 = 1/z0, Z1 = 1/z1, nz0 = Z0, nz1 = Z1, dz = Z1 - Z0,
23757         &zleft = xdir?nz0:nz1,
23758         &zright = xdir?nz1:nz0,
23759         &zup = ydir?nz0:nz1,
23760         &zdown = ydir?nz1:nz0;
23761       if (xright<0 || xleft>=width()) return *this;
23762       if (xleft<0) {
23763         const int D = xright - xleft;
23764         yleft-=xleft*(yright - yleft)/D;
23765         zleft-=xleft*(zright - zleft)/D;
23766         xleft = 0;
23767       }
23768       if (xright>=width()) {
23769         const int d = xright - width(), D = xright - xleft;
23770         yright-=d*(yright - yleft)/D;
23771         zright-=d*(zright - zleft)/D;
23772         xright = width()-1;
23773       }
23774       if (ydown<0 || yup>=height()) return *this;
23775       if (yup<0) {
23776         const int D = ydown - yup;
23777         xup-=yup*(xdown - xup)/D;
23778         zup-=yup*(zdown - zup)/D;
23779         yup = 0;
23780       }
23781       if (ydown>=height()) {
23782         const int d = ydown - height(), D = ydown - yup;
23783         xdown-=d*(xdown - xup)/D;
23784         zdown-=d*(zdown - zup)/D;
23785         ydown = height()-1;
23786       }
23787       T *ptrd0 = data(nx0,ny0);
23788       float *ptrz = zbuffer.data(nx0,ny0);
23789       int dx = xright - xleft, dy = ydown - yup;
23790       const bool steep = dy>dx;
23791       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
23792       const int
23793         offx = (nx0<nx1?1:-1)*(steep?_width:1),
23794         offy = (ny0<ny1?1:-1)*(steep?1:_width),
23795         wh = _width*_height,
23796         ndx = dx>0?dx:1;
23797       if (opacity>=1) {
23798         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
23799           const float z = Z0 + x*dz/ndx;
23800           if (z>=*ptrz && pattern&hatch) {
23801             *ptrz = z;
23802             T *ptrd = ptrd0; const tc *col = color;
23803             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
23804           }
23805           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
23806           ptrd0+=offx; ptrz+=offx;
23807           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
23808         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
23809           const float z = Z0 + x*dz/ndx;
23810           if (z>=*ptrz) {
23811             *ptrz = z;
23812             T *ptrd = ptrd0; const tc *col = color;
23813             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
23814           }
23815           ptrd0+=offx; ptrz+=offx;
23816           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
23817         }
23818       } else {
23819         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
23820         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
23821           const float z = Z0 + x*dz/ndx;
23822           if (z>=*ptrz && pattern&hatch) {
23823             *ptrz = z;
23824             T *ptrd = ptrd0; const tc *col = color;
23825             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
23826           }
23827           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
23828           ptrd0+=offx; ptrz+=offx;
23829           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
23830         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
23831           const float z = Z0 + x*dz/ndx;
23832           if (z>=*ptrz) {
23833             *ptrz = z;
23834             T *ptrd = ptrd0; const tc *col = color;
23835             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
23836           }
23837           ptrd0+=offx; ptrz+=offx;
23838           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
23839         }
23840       }
23841       return *this;
23842     }
23843 
23844     //! Draw a 3d colored line.
23845     template<typename tc>
23846     CImg<T>& draw_line(const int x0, const int y0, const int z0,
23847                        const int x1, const int y1, const int z1,
23848                        const tc *const color, const float opacity=1,
23849                        const unsigned int pattern=~0U, const bool init_hatch=true) {
23850       if (!color)
23851         throw CImgArgumentException(_cimg_instance
23852                                     "draw_line() : Specified color is (null).",
23853                                     cimg_instance);
23854 
23855       if (is_empty()) return *this;
23856       static unsigned int hatch = ~0U - (~0U>>1);
23857       if (init_hatch) hatch = ~0U - (~0U>>1);
23858       int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1;
23859       if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
23860       if (nx1<0 || nx0>=width()) return *this;
23861       if (nx0<0) { const int D = 1 + nx1 - nx0; ny0-=nx0*(1 + ny1 - ny0)/D; nz0-=nx0*(1 + nz1 - nz0)/D; nx0 = 0; }
23862       if (nx1>=width()) { const int d = nx1-width(), D = 1 + nx1 - nx0; ny1+=d*(1 + ny0 - ny1)/D; nz1+=d*(1 + nz0 - nz1)/D; nx1 = width()-1; }
23863       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
23864       if (ny1<0 || ny0>=height()) return *this;
23865       if (ny0<0) { const int D = 1 + ny1 - ny0; nx0-=ny0*(1 + nx1 - nx0)/D; nz0-=ny0*(1 + nz1 - nz0)/D; ny0 = 0; }
23866       if (ny1>=height()) { const int d = ny1-height(), D = 1 + ny1 - ny0; nx1+=d*(1 + nx0 - nx1)/D; nz1+=d*(1 + nz0 - nz1)/D; ny1 = height()-1; }
23867       if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
23868       if (nz1<0 || nz0>=depth()) return *this;
23869       if (nz0<0) { const int D = 1 + nz1 - nz0; nx0-=nz0*(1 + nx1 - nx0)/D; ny0-=nz0*(1 + ny1 - ny0)/D; nz0 = 0; }
23870       if (nz1>=depth()) { const int d = nz1-depth(), D = 1 + nz1 - nz0; nx1+=d*(1 + nx0 - nx1)/D; ny1+=d*(1 + ny0 - ny1)/D; nz1 = depth()-1; }
23871       const unsigned int dmax = cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0), whd = _width*_height*_depth;
23872       const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax;
23873       float x = (float)nx0, y = (float)ny0, z = (float)nz0;
23874       if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) {
23875         if (!(~pattern) || (~pattern && pattern&hatch)) {
23876           T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
23877           const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
23878         }
23879         x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
23880       } else {
23881         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
23882         for (unsigned int t = 0; t<=dmax; ++t) {
23883           if (!(~pattern) || (~pattern && pattern&hatch)) {
23884             T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
23885             const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
23886           }
23887           x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
23888         }
23889       }
23890       return *this;
23891     }
23892 
23893     //! Draw a 2d textured line.
23894     /**
23895        \param x0 X-coordinate of the starting line point.
23896        \param y0 Y-coordinate of the starting line point.
23897        \param x1 X-coordinate of the ending line point.
23898        \param y1 Y-coordinate of the ending line point.
23899        \param texture Texture image defining the pixel colors.
23900        \param tx0 X-coordinate of the starting texture point.
23901        \param ty0 Y-coordinate of the starting texture point.
23902        \param tx1 X-coordinate of the ending texture point.
23903        \param ty1 Y-coordinate of the ending texture point.
23904        \param opacity Drawing opacity (optional).
23905        \param pattern An integer whose bits describe the line pattern (optional).
23906        \param init_hatch Flag telling if the hash variable must be reinitialized (optional).
23907        \note
23908        - Clipping is supported but not for texture coordinates.
23909        - Line routine uses the well known Bresenham's algorithm.
23910        \par Example:
23911        \code
23912        CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
23913        const unsigned char color[] = { 255,128,64 };
23914        img.draw_line(40,40,80,70,texture,0,0,255,255);
23915        \endcode
23916     **/
23917     template<typename tc>
23918     CImg<T>& draw_line(const int x0, const int y0,
23919                        const int x1, const int y1,
23920                        const CImg<tc>& texture,
23921                        const int tx0, const int ty0,
23922                        const int tx1, const int ty1,
23923                        const float opacity=1,
23924                        const unsigned int pattern=~0U, const bool init_hatch=true) {
23925       if (texture._depth>1 || texture._spectrum<_spectrum)
23926         throw CImgArgumentException(_cimg_instance
23927                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
23928                                     cimg_instance,
23929                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
23930 
23931       if (is_empty()) return *this;
23932       if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
23933       static unsigned int hatch = ~0U - (~0U>>1);
23934       if (init_hatch) hatch = ~0U - (~0U>>1);
23935       const bool xdir = x0<x1, ydir = y0<y1;
23936       int
23937         dtx = tx1-tx0, dty = ty1-ty0,
23938         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
23939         tnx0 = tx0, tnx1 = tx1, tny0 = ty0, tny1 = ty1,
23940         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
23941         &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
23942         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0,
23943         &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
23944       if (xright<0 || xleft>=width()) return *this;
23945       if (xleft<0) {
23946         const int D = xright - xleft;
23947         yleft-=xleft*(yright - yleft)/D;
23948         txleft-=xleft*(txright - txleft)/D;
23949         tyleft-=xleft*(tyright - tyleft)/D;
23950         xleft = 0;
23951       }
23952       if (xright>=width()) {
23953         const int d = xright - width(), D = xright - xleft;
23954         yright-=d*(yright - yleft)/D;
23955         txright-=d*(txright - txleft)/D;
23956         tyright-=d*(tyright - tyleft)/D;
23957         xright = width()-1;
23958       }
23959       if (ydown<0 || yup>=height()) return *this;
23960       if (yup<0) {
23961         const int D = ydown - yup;
23962         xup-=yup*(xdown - xup)/D;
23963         txup-=yup*(txdown - txup)/D;
23964         tyup-=yup*(tydown - tyup)/D;
23965         yup = 0;
23966       }
23967       if (ydown>=height()) {
23968         const int d = ydown - height(), D = ydown - yup;
23969         xdown-=d*(xdown - xup)/D;
23970         txdown-=d*(txdown - txup)/D;
23971         tydown-=d*(tydown - tyup)/D;
23972         ydown = height()-1;
23973       }
23974       T *ptrd0 = data(nx0,ny0);
23975       int dx = xright - xleft, dy = ydown - yup;
23976       const bool steep = dy>dx;
23977       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
23978       const int
23979         offx = (nx0<nx1?1:-1)*(steep?_width:1),
23980         offy = (ny0<ny1?1:-1)*(steep?1:_width),
23981         wh = _width*_height,
23982         ndx = dx>0?dx:1;
23983       if (opacity>=1) {
23984         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
23985           if (pattern&hatch) {
23986             T *ptrd = ptrd0;
23987             const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
23988             cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
23989           }
23990           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
23991           ptrd0+=offx;
23992           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
23993         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
23994           T *ptrd = ptrd0;
23995           const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
23996           cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
23997           ptrd0+=offx;
23998           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
23999         }
24000       } else {
24001         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
24002         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
24003           T *ptrd = ptrd0;
24004           if (pattern&hatch) {
24005             const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
24006             cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
24007           }
24008           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
24009           ptrd0+=offx;
24010           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
24011         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
24012           T *ptrd = ptrd0;
24013           const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
24014           cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
24015           ptrd0+=offx;
24016           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
24017         }
24018       }
24019       return *this;
24020     }
24021 
24022     //! Draw a 2d textured line, with perspective correction.
24023     template<typename tc>
24024     CImg<T>& draw_line(const int x0, const int y0, const float z0,
24025                        const int x1, const int y1, const float z1,
24026                        const CImg<tc>& texture,
24027                        const int tx0, const int ty0,
24028                        const int tx1, const int ty1,
24029                        const float opacity=1,
24030                        const unsigned int pattern=~0U, const bool init_hatch=true) {
24031       if (texture._depth>1 || texture._spectrum<_spectrum)
24032         throw CImgArgumentException(_cimg_instance
24033                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
24034                                     cimg_instance,
24035                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
24036 
24037       if (is_empty() && z0<=0 && z1<=0) return *this;
24038       if (is_overlapped(texture)) return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
24039       static unsigned int hatch = ~0U - (~0U>>1);
24040       if (init_hatch) hatch = ~0U - (~0U>>1);
24041       const bool xdir = x0<x1, ydir = y0<y1;
24042       int
24043         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
24044         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
24045         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
24046         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
24047         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
24048       float
24049         Tx0 = tx0/z0, Tx1 = tx1/z1,
24050         Ty0 = ty0/z0, Ty1 = ty1/z1,
24051         Z0 = 1/z0, Z1 = 1/z1,
24052         dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
24053         tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
24054         &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
24055         &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
24056         &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
24057         &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
24058       if (xright<0 || xleft>=width()) return *this;
24059       if (xleft<0) {
24060         const int D = xright - xleft;
24061         yleft-=xleft*(yright - yleft)/D;
24062         zleft-=xleft*(zright - zleft)/D;
24063         txleft-=xleft*(txright - txleft)/D;
24064         tyleft-=xleft*(tyright - tyleft)/D;
24065         xleft = 0;
24066       }
24067       if (xright>=width()) {
24068         const int d = xright - width(), D = xright - xleft;
24069         yright-=d*(yright - yleft)/D;
24070         zright-=d*(zright - zleft)/D;
24071         txright-=d*(txright - txleft)/D;
24072         tyright-=d*(tyright - tyleft)/D;
24073         xright = width()-1;
24074       }
24075       if (ydown<0 || yup>=height()) return *this;
24076       if (yup<0) {
24077         const int D = ydown - yup;
24078         xup-=yup*(xdown - xup)/D;
24079         zup-=yup*(zdown - zup)/D;
24080         txup-=yup*(txdown - txup)/D;
24081         tyup-=yup*(tydown - tyup)/D;
24082         yup = 0;
24083       }
24084       if (ydown>=height()) {
24085         const int d = ydown - height(), D = ydown - yup;
24086         xdown-=d*(xdown - xup)/D;
24087         zdown-=d*(zdown - zup)/D;
24088         txdown-=d*(txdown - txup)/D;
24089         tydown-=d*(tydown - tyup)/D;
24090         ydown = height()-1;
24091       }
24092       T *ptrd0 = data(nx0,ny0);
24093       int dx = xright - xleft, dy = ydown - yup;
24094       const bool steep = dy>dx;
24095       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
24096       const int
24097         offx = (nx0<nx1?1:-1)*(steep?_width:1),
24098         offy = (ny0<ny1?1:-1)*(steep?1:_width),
24099         wh = _width*_height,
24100         ndx = dx>0?dx:1;
24101       if (opacity>=1) {
24102         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
24103           if (pattern&hatch) {
24104             const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
24105             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
24106           }
24107           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
24108           ptrd0+=offx;
24109           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
24110         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
24111           const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
24112           T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
24113           ptrd0+=offx;
24114           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
24115         }
24116       } else {
24117         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
24118         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
24119           if (pattern&hatch) {
24120             const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
24121             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
24122           }
24123           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
24124           ptrd0+=offx;
24125           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
24126         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
24127           const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
24128           T *ptrd = ptrd0;
24129           cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
24130           ptrd0+=offx;
24131           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
24132         }
24133       }
24134       return *this;
24135     }
24136 
24137     //! Draw a 2d textured line, with z-buffering and perspective correction.
24138     template<typename tc>
24139     CImg<T>& draw_line(CImg<floatT>& zbuffer,
24140                        const int x0, const int y0, const float z0,
24141                        const int x1, const int y1, const float z1,
24142                        const CImg<tc>& texture,
24143                        const int tx0, const int ty0,
24144                        const int tx1, const int ty1,
24145                        const float opacity=1,
24146                        const unsigned int pattern=~0U, const bool init_hatch=true) {
24147       if (!is_sameXY(zbuffer))
24148         throw CImgArgumentException(_cimg_instance
24149                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
24150                                     cimg_instance,
24151                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
24152 
24153       if (texture._depth>1 || texture._spectrum<_spectrum)
24154         throw CImgArgumentException(_cimg_instance
24155                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
24156                                     cimg_instance,
24157                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
24158 
24159       if (is_empty() || z0<=0 || z1<=0) return *this;
24160       if (is_overlapped(texture)) return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
24161       static unsigned int hatch = ~0U - (~0U>>1);
24162       if (init_hatch) hatch = ~0U - (~0U>>1);
24163       const bool xdir = x0<x1, ydir = y0<y1;
24164       int
24165         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
24166         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
24167         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
24168         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
24169         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
24170       float
24171         Tx0 = tx0/z0, Tx1 = tx1/z1,
24172         Ty0 = ty0/z0, Ty1 = ty1/z1,
24173         Z0 = 1/z0, Z1 = 1/z1,
24174         dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
24175         tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
24176         &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
24177         &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
24178         &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
24179         &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
24180       if (xright<0 || xleft>=width()) return *this;
24181       if (xleft<0) {
24182         const int D = xright - xleft;
24183         yleft-=xleft*(yright - yleft)/D;
24184         zleft-=xleft*(zright - zleft)/D;
24185         txleft-=xleft*(txright - txleft)/D;
24186         tyleft-=xleft*(tyright - tyleft)/D;
24187         xleft = 0;
24188       }
24189       if (xright>=width()) {
24190         const int d = xright - width(), D = xright - xleft;
24191         yright-=d*(yright - yleft)/D;
24192         zright-=d*(zright - zleft)/D;
24193         txright-=d*(txright - txleft)/D;
24194         tyright-=d*(tyright - tyleft)/D;
24195         xright = width()-1;
24196       }
24197       if (ydown<0 || yup>=height()) return *this;
24198       if (yup<0) {
24199         const int D = ydown - yup;
24200         xup-=yup*(xdown - xup)/D;
24201         zup-=yup*(zdown - zup)/D;
24202         txup-=yup*(txdown - txup)/D;
24203         tyup-=yup*(tydown - tyup)/D;
24204         yup = 0;
24205       }
24206       if (ydown>=height()) {
24207         const int d = ydown - height(), D = ydown - yup;
24208         xdown-=d*(xdown - xup)/D;
24209         zdown-=d*(zdown - zup)/D;
24210         txdown-=d*(txdown - txup)/D;
24211         tydown-=d*(tydown - tyup)/D;
24212         ydown = height()-1;
24213       }
24214       T *ptrd0 = data(nx0,ny0);
24215       float *ptrz = zbuffer.data(nx0,ny0);
24216       int dx = xright - xleft, dy = ydown - yup;
24217       const bool steep = dy>dx;
24218       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
24219       const int
24220         offx = (nx0<nx1?1:-1)*(steep?_width:1),
24221         offy = (ny0<ny1?1:-1)*(steep?1:_width),
24222         wh = _width*_height,
24223         ndx = dx>0?dx:1;
24224       if (opacity>=1) {
24225         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
24226           if (pattern&hatch) {
24227             const float z = Z0 + x*dz/ndx;
24228             if (z>=*ptrz) {
24229               *ptrz = z;
24230               const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
24231               T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
24232             }
24233           }
24234           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
24235           ptrd0+=offx; ptrz+=offx;
24236           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
24237         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
24238           const float z = Z0 + x*dz/ndx;
24239           if (z>=*ptrz) {
24240             *ptrz = z;
24241             const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
24242             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
24243           }
24244           ptrd0+=offx; ptrz+=offx;
24245           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
24246         }
24247       } else {
24248         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
24249         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
24250           if (pattern&hatch) {
24251             const float z = Z0 + x*dz/ndx;
24252             if (z>=*ptrz) {
24253               *ptrz = z;
24254               const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
24255               T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
24256             }
24257           }
24258           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
24259           ptrd0+=offx; ptrz+=offx;
24260           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
24261         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
24262           const float z = Z0 + x*dz/ndx;
24263           if (z>=*ptrz) {
24264             *ptrz = z;
24265             const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
24266             T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
24267           }
24268           ptrd0+=offx; ptrz+=offx;
24269           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offx; error+=dx; }
24270         }
24271       }
24272       return *this;
24273     }
24274 
24275     //! Draw a set of consecutive colored lines in the instance image.
24276     /**
24277        \param points Coordinates of vertices, stored as a list of vectors.
24278        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
24279        \param opacity Drawing opacity (optional).
24280        \param pattern An integer whose bits describe the line pattern (optional).
24281        \param init_hatch If set to true, init hatch motif.
24282        \note
24283        - This function uses several call to the single CImg::draw_line() procedure,
24284        depending on the vectors size in \p points.
24285        \par Example:
24286        \code
24287        CImg<unsigned char> img(100,100,1,3,0);
24288        const unsigned char color[] = { 255,128,64 };
24289        CImgList<int> points;
24290        points.insert(CImg<int>::vector(0,0)).
24291              .insert(CImg<int>::vector(70,10)).
24292              .insert(CImg<int>::vector(80,60)).
24293              .insert(CImg<int>::vector(10,90));
24294        img.draw_line(points,color);
24295        \endcode
24296     **/
24297     template<typename t, typename tc>
24298     CImg<T>& draw_line(const CImg<t>& points,
24299                        const tc *const color, const float opacity=1,
24300                        const unsigned int pattern=~0U, const bool init_hatch=true) {
24301       if (is_empty() || !points || points._width<2) return *this;
24302       bool ninit_hatch = init_hatch;
24303       switch (points._height) {
24304       case 0 : case 1 :
24305         throw CImgArgumentException(_cimg_instance
24306                                     "draw_line() : Invalid specified point set (%u,%u,%u,%u,%p).",
24307                                     cimg_instance,
24308                                     points._width,points._height,points._depth,points._spectrum,points._data);
24309 
24310       case 2 : {
24311         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
24312         int ox = x0, oy = y0;
24313         for (unsigned int i = 1; i<points._width; ++i) {
24314           const int x = (int)points(i,0), y = (int)points(i,1);
24315           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
24316           ninit_hatch = false;
24317           ox = x; oy = y;
24318         }
24319       } break;
24320       default : {
24321         const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
24322         int ox = x0, oy = y0, oz = z0;
24323         for (unsigned int i = 1; i<points._width; ++i) {
24324           const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
24325           draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
24326           ninit_hatch = false;
24327           ox = x; oy = y; oz = z;
24328         }
24329       }
24330       }
24331       return *this;
24332     }
24333 
24334     //! Draw a colored arrow in the instance image.
24335     /**
24336        \param x0 X-coordinate of the starting arrow point (tail).
24337        \param y0 Y-coordinate of the starting arrow point (tail).
24338        \param x1 X-coordinate of the ending arrow point (head).
24339        \param y1 Y-coordinate of the ending arrow point (head).
24340        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
24341        \param angle Aperture angle of the arrow head (optional).
24342        \param length Length of the arrow head. If negative, describes a percentage of the arrow length (optional).
24343        \param opacity Drawing opacity (optional).
24344        \param pattern An integer whose bits describe the line pattern (optional).
24345        \note
24346        - Clipping is supported.
24347     **/
24348     template<typename tc>
24349     CImg<T>& draw_arrow(const int x0, const int y0,
24350                         const int x1, const int y1,
24351                         const tc *const color, const float opacity=1,
24352                         const float angle=30, const float length=-10,
24353                         const unsigned int pattern=~0U) {
24354       if (is_empty()) return *this;
24355       const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
24356         deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f,
24357         l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
24358       if (sq>0) {
24359         const float
24360             cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
24361             cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
24362         const int
24363           xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
24364           xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
24365           xc = x1 + (int)((l+1)*(cl+cr))/2, yc = y1 + (int)((l+1)*(sl+sr))/2;
24366         draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
24367       } else draw_point(x0,y0,color,opacity);
24368       return *this;
24369     }
24370 
24371     //! Draw a cubic spline curve in the instance image.
24372     /**
24373        \param x0 X-coordinate of the starting curve point
24374        \param y0 Y-coordinate of the starting curve point
24375        \param u0 X-coordinate of the starting velocity
24376        \param v0 Y-coordinate of the starting velocity
24377        \param x1 X-coordinate of the ending curve point
24378        \param y1 Y-coordinate of the ending curve point
24379        \param u1 X-coordinate of the ending velocity
24380        \param v1 Y-coordinate of the ending velocity
24381        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
24382        \param precision Curve drawing precision (optional).
24383        \param opacity Drawing opacity (optional).
24384        \param pattern An integer whose bits describe the line pattern (optional).
24385        \param init_hatch If \c true, init hatch motif.
24386        \note
24387        - The curve is a 2d cubic Bezier spline, from the set of specified starting/ending points
24388        and corresponding velocity vectors.
24389        - The spline is drawn as a serie of connected segments. The \p precision parameter sets the
24390        average number of pixels in each drawn segment.
24391        - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), (\p xb,\p yb), (\p x1,\p y1) }
24392        where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point and (\p xa,\p ya), (\p xb,\p yb) are two
24393        \e control points.
24394        The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from the control points as
24395        \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb).
24396        \par Example:
24397        \code
24398        CImg<unsigned char> img(100,100,1,3,0);
24399        const unsigned char color[] = { 255,255,255 };
24400        img.draw_spline(30,30,0,100,90,40,0,-100,color);
24401        \endcode
24402     **/
24403     template<typename tc>
24404     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
24405                          const int x1, const int y1, const float u1, const float v1,
24406                          const tc *const color, const float opacity=1,
24407                          const float precision=4, const unsigned int pattern=~0U,
24408                          const bool init_hatch=true) {
24409       if (!color)
24410         throw CImgArgumentException(_cimg_instance
24411                                     "draw_spline() : Specified color is (null).",
24412                                     cimg_instance);
24413 
24414       if (is_empty()) return *this;
24415       bool ninit_hatch = init_hatch;
24416       const float
24417         dx = (float)(x1 - x0),
24418         dy = (float)(y1 - y0),
24419         dmax = cimg::max(cimg::abs(dx),cimg::abs(dy)),
24420         ax = -2*dx + u0 + u1,
24421         bx = 3*dx - 2*u0 - u1,
24422         ay = -2*dy + v0 + v1,
24423         by = 3*dy - 2*v0 - v1,
24424         xprecision = dmax>0?precision/dmax:1.0f,
24425         tmax = 1 + (dmax>0?xprecision:0.0f);
24426       int ox = x0, oy = y0;
24427       for (float t = 0; t<tmax; t+=xprecision) {
24428         const float
24429           t2 = t*t,
24430           t3 = t2*t;
24431         const int
24432           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
24433           ny = (int)(ay*t3 + by*t2 + v0*t + y0);
24434         draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
24435         ninit_hatch = false;
24436         ox = nx; oy = ny;
24437       }
24438       return *this;
24439     }
24440 
24441     //! Draw a cubic spline curve in the instance image (for volumetric images).
24442     /**
24443        \note
24444        - Similar to CImg::draw_spline() for a 3d spline in a volumetric image.
24445     **/
24446     template<typename tc>
24447     CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0,
24448                          const int x1, const int y1, const int z1, const float u1, const float v1, const float w1,
24449                          const tc *const color, const float opacity=1,
24450                          const float precision=4, const unsigned int pattern=~0U,
24451                          const bool init_hatch=true) {
24452       if (!color)
24453         throw CImgArgumentException(_cimg_instance
24454                                     "draw_spline() : Specified color is (null).",
24455                                     cimg_instance);
24456 
24457       if (is_empty()) return *this;
24458       bool ninit_hatch = init_hatch;
24459       const float
24460         dx = (float)(x1 - x0),
24461         dy = (float)(y1 - y0),
24462         dz = (float)(z1 - z0),
24463         dmax = cimg::max(cimg::abs(dx),cimg::abs(dy),cimg::abs(dz)),
24464         ax = -2*dx + u0 + u1,
24465         bx = 3*dx - 2*u0 - u1,
24466         ay = -2*dy + v0 + v1,
24467         by = 3*dy - 2*v0 - v1,
24468         az = -2*dz + w0 + w1,
24469         bz = 3*dz - 2*w0 - w1,
24470         xprecision = dmax>0?precision/dmax:1.0f,
24471         tmax = 1 + (dmax>0?xprecision:0.0f);
24472       int ox = x0, oy = y0, oz = z0;
24473       for (float t = 0; t<tmax; t+=xprecision) {
24474         const float
24475           t2 = t*t,
24476           t3 = t2*t;
24477         const int
24478           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
24479           ny = (int)(ay*t3 + by*t2 + v0*t + y0),
24480           nz = (int)(az*t3 + bz*t2 + w0*t + z0);
24481         draw_line(ox,oy,oz,nx,ny,nz,color,opacity,pattern,ninit_hatch);
24482         ninit_hatch = false;
24483         ox = nx; oy = ny; oz = nz;
24484       }
24485       return *this;
24486     }
24487 
24488     //! Draw a cubic spline curve in the instance image.
24489     /**
24490        \param x0 X-coordinate of the starting curve point
24491        \param y0 Y-coordinate of the starting curve point
24492        \param u0 X-coordinate of the starting velocity
24493        \param v0 Y-coordinate of the starting velocity
24494        \param x1 X-coordinate of the ending curve point
24495        \param y1 Y-coordinate of the ending curve point
24496        \param u1 X-coordinate of the ending velocity
24497        \param v1 Y-coordinate of the ending velocity
24498        \param texture Texture image defining line pixel colors.
24499        \param tx0 X-coordinate of the starting texture point.
24500        \param ty0 Y-coordinate of the starting texture point.
24501        \param tx1 X-coordinate of the ending texture point.
24502        \param ty1 Y-coordinate of the ending texture point.
24503        \param precision Curve drawing precision (optional).
24504        \param opacity Drawing opacity (optional).
24505        \param pattern An integer whose bits describe the line pattern (optional).
24506        \param init_hatch if \c true, reinit hatch motif.
24507     **/
24508     template<typename t>
24509     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
24510                          const int x1, const int y1, const float u1, const float v1,
24511                          const CImg<t>& texture,
24512                          const int tx0, const int ty0, const int tx1, const int ty1,
24513                          const float opacity=1,
24514                          const float precision=4, const unsigned int pattern=~0U,
24515                          const bool init_hatch=true) {
24516       if (texture._depth>1 || texture._spectrum<_spectrum)
24517         throw CImgArgumentException(_cimg_instance
24518                                     "draw_line() : Invalid specified texture (%u,%u,%u,%u,%p).",
24519                                     cimg_instance,
24520                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
24521 
24522       if (is_empty()) return *this;
24523       if (is_overlapped(texture)) return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
24524       bool ninit_hatch = true;
24525       const float
24526         dx = (float)(x1 - x0),
24527         dy = (float)(y1 - y0),
24528         dmax = cimg::max(cimg::abs(dx),cimg::abs(dy)),
24529         ax = -2*dx + u0 + u1,
24530         bx = 3*dx - 2*u0 - u1,
24531         ay = -2*dy + v0 + v1,
24532         by = 3*dy - 2*v0 - v1,
24533         xprecision = dmax>0?precision/dmax:1.0f,
24534         tmax = 1 + (dmax>0?xprecision:0.0f);
24535       int ox = x0, oy = y0, otx = tx0, oty = ty0;
24536       for (float t1 = 0; t1<tmax; t1+=xprecision) {
24537         const float
24538           t2 = t1*t1,
24539           t3 = t2*t1;
24540         const int
24541           nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
24542           ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
24543           ntx = tx0 + (int)((tx1-tx0)*t1/tmax),
24544           nty = ty0 + (int)((ty1-ty0)*t1/tmax);
24545         draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
24546         ninit_hatch = false;
24547         ox = nx; oy = ny; otx = ntx; oty = nty;
24548       }
24549       return *this;
24550     }
24551 
24552     // Draw a set of connected spline curves in the instance image (internal).
24553     template<typename tp, typename tt, typename tc>
24554     CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
24555                          const tc *const color, const float opacity=1,
24556                          const bool close_set=false, const float precision=4,
24557                          const unsigned int pattern=~0U, const bool init_hatch=true) {
24558       if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
24559       bool ninit_hatch = init_hatch;
24560       switch (points._height) {
24561       case 0 : case 1 :
24562         throw CImgArgumentException(_cimg_instance
24563                                     "draw_spline() : Invalid specified point set (%u,%u,%u,%u,%p).",
24564                                     cimg_instance,
24565                                     points._width,points._height,points._depth,points._spectrum,points._data);
24566 
24567       case 2 : {
24568         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
24569         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
24570         int ox = x0, oy = y0;
24571         float ou = u0, ov = v0;
24572         for (unsigned int i = 1; i<points._width; ++i) {
24573           const int x = (int)points(i,0), y = (int)points(i,1);
24574           const float u = (float)tangents(i,0), v = (float)tangents(i,1);
24575           draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
24576           ninit_hatch = false;
24577           ox = x; oy = y; ou = u; ov = v;
24578         }
24579         if (close_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
24580       } break;
24581       default : {
24582         const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
24583         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1), w0 = (float)tangents(0,2);
24584         int ox = x0, oy = y0, oz = z0;
24585         float ou = u0, ov = v0, ow = w0;
24586         for (unsigned int i = 1; i<points._width; ++i) {
24587           const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
24588           const float u = (float)tangents(i,0), v = (float)tangents(i,1), w = (float)tangents(i,2);
24589           draw_spline(ox,oy,oz,ou,ov,ow,x,y,z,u,v,w,color,opacity,pattern,ninit_hatch);
24590           ninit_hatch = false;
24591           ox = x; oy = y; oz = z; ou = u; ov = v; ow = w;
24592         }
24593         if (close_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false);
24594       }
24595       }
24596       return *this;
24597     }
24598 
24599     //! Draw a set of consecutive colored splines in the instance image.
24600     template<typename tp, typename tc>
24601     CImg<T>& draw_spline(const CImg<tp>& points,
24602                          const tc *const color, const float opacity=1,
24603                          const bool close_set=false, const float precision=4,
24604                          const unsigned int pattern=~0U, const bool init_hatch=true) {
24605       if (is_empty() || !points || points._width<2) return *this;
24606       CImg<Tfloat> tangents;
24607       switch (points._height) {
24608       case 0 : case 1 :
24609         throw CImgArgumentException(_cimg_instance
24610                                     "draw_spline() : Invalid specified point set (%u,%u,%u,%u,%p).",
24611                                     cimg_instance,
24612                                     points._width,points._height,points._depth,points._spectrum,points._data);
24613       case 2 : {
24614         tangents.assign(points._width,points._height);
24615         cimg_forX(points,p) {
24616           const unsigned int
24617             p0 = close_set?(p+points._width-1)%points._width:(p?p-1:0),
24618             p1 = close_set?(p+1)%points._width:(p+1<points._width?p+1:p);
24619           const float
24620             x = (float)points(p,0),
24621             y = (float)points(p,1),
24622             x0 = (float)points(p0,0),
24623             y0 = (float)points(p0,1),
24624             x1 = (float)points(p1,0),
24625             y1 = (float)points(p1,1),
24626             u0 = x - x0,
24627             v0 = y - y0,
24628             n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0),
24629             u1 = x1 - x,
24630             v1 = y1 - y,
24631             n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1),
24632             u = u0/n0 + u1/n1,
24633             v = v0/n0 + v1/n1,
24634             n = 1e-8f + (float)std::sqrt(u*u + v*v),
24635             fact = 0.5f*(n0 + n1);
24636           tangents(p,0) = (Tfloat)(fact*u/n);
24637           tangents(p,1) = (Tfloat)(fact*v/n);
24638         }
24639       } break;
24640       default : {
24641         tangents.assign(points._width,points._height);
24642         cimg_forX(points,p) {
24643           const unsigned int
24644             p0 = close_set?(p+points._width-1)%points._width:(p?p-1:0),
24645             p1 = close_set?(p+1)%points._width:(p+1<points._width?p+1:p);
24646           const float
24647             x = (float)points(p,0),
24648             y = (float)points(p,1),
24649             z = (float)points(p,2),
24650             x0 = (float)points(p0,0),
24651             y0 = (float)points(p0,1),
24652             z0 = (float)points(p0,2),
24653             x1 = (float)points(p1,0),
24654             y1 = (float)points(p1,1),
24655             z1 = (float)points(p1,2),
24656             u0 = x - x0,
24657             v0 = y - y0,
24658             w0 = z - z0,
24659             n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0 + w0*w0),
24660             u1 = x1 - x,
24661             v1 = y1 - y,
24662             w1 = z1 - z,
24663             n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1 + w1*w1),
24664             u = u0/n0 + u1/n1,
24665             v = v0/n0 + v1/n1,
24666             w = w0/n0 + w1/n1,
24667             n = 1e-8f + (float)std::sqrt(u*u + v*v + w*w),
24668             fact = 0.5f*(n0 + n1);
24669           tangents(p,0) = (Tfloat)(fact*u/n);
24670           tangents(p,1) = (Tfloat)(fact*v/n);
24671           tangents(p,2) = (Tfloat)(fact*w/n);
24672         }
24673       }
24674       }
24675       return draw_spline(points,tangents,color,opacity,close_set,precision,pattern,init_hatch);
24676     }
24677 
24678     // Inner macro for drawing triangles.
24679 #define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \
24680         for (int y = y0<0?0:y0, \
24681                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
24682                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
24683                _sxn=1, \
24684                _sxr=1, \
24685                _sxl=1, \
24686                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
24687                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
24688                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
24689                _dyn = y2-y1, \
24690                _dyr = y2-y0, \
24691                _dyl = y1-y0, \
24692                _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
24693                            _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
24694                            _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
24695                            cimg::min((int)(img)._height-y-1,y2-y)), \
24696                _errn = _dyn/2, \
24697                _errr = _dyr/2, \
24698                _errl = _dyl/2, \
24699                _rxn = _dyn?(x2-x1)/_dyn:0, \
24700                _rxr = _dyr?(x2-x0)/_dyr:0, \
24701                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
24702                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \
24703              _counter>=0; --_counter, ++y, \
24704                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
24705                xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \
24706                            (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
24707 
24708 #define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \
24709         for (int y = y0<0?0:y0, \
24710                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
24711                cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
24712                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
24713                cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
24714                _sxn=1, _scn=1, \
24715                _sxr=1, _scr=1, \
24716                _sxl=1, _scl=1, \
24717                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
24718                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
24719                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
24720                _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
24721                _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
24722                _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
24723                _dyn = y2-y1, \
24724                _dyr = y2-y0, \
24725                _dyl = y1-y0, \
24726                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
24727                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
24728                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
24729                           _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
24730                           _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
24731                           _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
24732                           cimg::min((int)(img)._height-y-1,y2-y)), \
24733                _errn = _dyn/2, _errcn = _errn, \
24734                _errr = _dyr/2, _errcr = _errr, \
24735                _errl = _dyl/2, _errcl = _errl, \
24736                _rxn = _dyn?(x2-x1)/_dyn:0, \
24737                _rcn = _dyn?(c2-c1)/_dyn:0, \
24738                _rxr = _dyr?(x2-x0)/_dyr:0, \
24739                _rcr = _dyr?(c2-c0)/_dyr:0, \
24740                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
24741                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
24742                _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
24743                                        (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \
24744              _counter>=0; --_counter, ++y, \
24745                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
24746                cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
24747                xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
24748                            _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
24749                (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
24750                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
24751 
24752 #define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \
24753         for (int y = y0<0?0:y0, \
24754                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
24755                txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
24756                tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
24757                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
24758                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
24759                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
24760                _sxn=1, _stxn=1, _styn=1, \
24761                _sxr=1, _stxr=1, _styr=1, \
24762                _sxl=1, _stxl=1, _styl=1, \
24763                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
24764                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
24765                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
24766                _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
24767                _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
24768                _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
24769                _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
24770                _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
24771                _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
24772                _dyn = y2-y1, \
24773                _dyr = y2-y0, \
24774                _dyl = y1-y0, \
24775                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
24776                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
24777                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
24778                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
24779                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
24780                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
24781                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
24782                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
24783                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
24784                           cimg::min((int)(img)._height-y-1,y2-y)), \
24785                _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \
24786                _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \
24787                _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \
24788                _rxn = _dyn?(x2-x1)/_dyn:0, \
24789                _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
24790                _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
24791                _rxr = _dyr?(x2-x0)/_dyr:0, \
24792                _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
24793                _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
24794                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
24795                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
24796                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
24797                                        (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
24798                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
24799                                        (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
24800              _counter>=0; --_counter, ++y, \
24801                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
24802                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
24803                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
24804                xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
24805                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
24806                            _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
24807                (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
24808                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\
24809                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
24810 
24811 #define _cimg_for_triangle4(img,xl,cl,txl,tyl,xr,cr,txr,tyr,y,x0,y0,c0,tx0,ty0,x1,y1,c1,tx1,ty1,x2,y2,c2,tx2,ty2) \
24812         for (int y = y0<0?0:y0, \
24813                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
24814                cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
24815                txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
24816                tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
24817                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
24818                cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
24819                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
24820                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
24821                _sxn=1, _scn=1, _stxn=1, _styn=1, \
24822                _sxr=1, _scr=1, _stxr=1, _styr=1, \
24823                _sxl=1, _scl=1, _stxl=1, _styl=1, \
24824                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
24825                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
24826                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
24827                _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
24828                _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
24829                _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
24830                _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
24831                _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
24832                _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
24833                _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
24834                _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
24835                _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
24836                _dyn = y2-y1, \
24837                _dyr = y2-y0, \
24838                _dyl = y1-y0, \
24839                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
24840                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
24841                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
24842                           _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
24843                           _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
24844                           _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
24845                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
24846                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
24847                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
24848                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
24849                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
24850                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
24851                           cimg::min((int)(img)._height-y-1,y2-y)), \
24852                _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \
24853                _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \
24854                _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \
24855                _rxn = _dyn?(x2-x1)/_dyn:0, \
24856                _rcn = _dyn?(c2-c1)/_dyn:0, \
24857                _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
24858                _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
24859                _rxr = _dyr?(x2-x0)/_dyr:0, \
24860                _rcr = _dyr?(c2-c0)/_dyr:0, \
24861                _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
24862                _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
24863                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
24864                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
24865                _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
24866                                        (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \
24867                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
24868                                         (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
24869                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
24870                                         (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
24871              _counter>=0; --_counter, ++y, \
24872                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
24873                cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
24874                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
24875                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
24876                xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
24877                             txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
24878                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
24879                             _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
24880                (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
24881                 _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
24882                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
24883                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
24884 
24885 #define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \
24886         for (int y = y0<0?0:y0, \
24887                xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
24888                txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
24889                tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
24890                lxr = y0>=0?lx0:(lx0-y0*(lx2-lx0)/(y2-y0)), \
24891                lyr = y0>=0?ly0:(ly0-y0*(ly2-ly0)/(y2-y0)), \
24892                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
24893                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
24894                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
24895                lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0-y0*(lx1-lx0)/(y1-y0))):(lx1-y1*(lx2-lx1)/(y2-y1)), \
24896                lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0-y0*(ly1-ly0)/(y1-y0))):(ly1-y1*(ly2-ly1)/(y2-y1)), \
24897                _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \
24898                _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \
24899                _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \
24900                _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), _dyn = y2-y1, \
24901                _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), _dyr = y2-y0, \
24902                _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), _dyl = y1-y0, \
24903                _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
24904                _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
24905                _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
24906                _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
24907                _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
24908                _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
24909                _dlxn = lx2>lx1?lx2-lx1:(_slxn=-1,lx1-lx2), \
24910                _dlxr = lx2>lx0?lx2-lx0:(_slxr=-1,lx0-lx2), \
24911                _dlxl = lx1>lx0?lx1-lx0:(_slxl=-1,lx0-lx1), \
24912                _dlyn = ly2>ly1?ly2-ly1:(_slyn=-1,ly1-ly2), \
24913                _dlyr = ly2>ly0?ly2-ly0:(_slyr=-1,ly0-ly2), \
24914                _dlyl = ly1>ly0?ly1-ly0:(_slyl=-1,ly0-ly1), \
24915                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
24916                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
24917                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
24918                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
24919                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
24920                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
24921                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
24922                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
24923                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
24924                           _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \
24925                           _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \
24926                           _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \
24927                           _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \
24928                           _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \
24929                           _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \
24930                           cimg::min((int)(img)._height-y-1,y2-y)), \
24931                _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \
24932                _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \
24933                _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \
24934                _rxn = _dyn?(x2-x1)/_dyn:0, \
24935                _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
24936                _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
24937                _rlxn = _dyn?(lx2-lx1)/_dyn:0, \
24938                _rlyn = _dyn?(ly2-ly1)/_dyn:0, \
24939                _rxr = _dyr?(x2-x0)/_dyr:0, \
24940                _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
24941                _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
24942                _rlxr = _dyr?(lx2-lx0)/_dyr:0, \
24943                _rlyr = _dyr?(ly2-ly0)/_dyr:0, \
24944                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
24945                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
24946                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
24947                                         (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
24948                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
24949                                         (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \
24950                _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1-lx0)/_dyl:0): \
24951                                         (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \
24952                _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1-ly0)/_dyl:0): \
24953                                         (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \
24954              _counter>=0; --_counter, ++y, \
24955                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
24956                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
24957                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
24958                lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \
24959                lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \
24960                xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
24961                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
24962                             lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \
24963                             lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \
24964                             _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
24965                (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
24966                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
24967                 _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \
24968                 _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \
24969                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
24970 
24971     // Draw a colored triangle (inner routine, uses bresenham's algorithm).
24972     template<typename tc>
24973     CImg<T>& _draw_triangle(const int x0, const int y0,
24974                             const int x1, const int y1,
24975                             const int x2, const int y2,
24976                             const tc *const color, const float opacity,
24977                             const float brightness) {
24978       _draw_scanline(color,opacity);
24979       const float nbrightness = brightness<0?0:(brightness>2?2:brightness);
24980       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
24981       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1);
24982       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2);
24983       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2);
24984       if (ny0<height() && ny2>=0) {
24985         if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0)
24986           _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xl,xr,y,color,opacity,nbrightness);
24987         else
24988           _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xr,xl,y,color,opacity,nbrightness);
24989       }
24990       return *this;
24991     }
24992 
24993     //! Draw a 2d filled colored triangle.
24994     template<typename tc>
24995     CImg<T>& draw_triangle(const int x0, const int y0,
24996                            const int x1, const int y1,
24997                            const int x2, const int y2,
24998                            const tc *const color, const float opacity=1) {
24999       if (!color)
25000         throw CImgArgumentException(_cimg_instance
25001                                     "draw_triangle : Specified color is (null).",
25002                                     cimg_instance);
25003 
25004       if (is_empty()) return *this;
25005       _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
25006       return *this;
25007     }
25008 
25009     //! Draw a 2d outlined colored triangle.
25010     template<typename tc>
25011     CImg<T>& draw_triangle(const int x0, const int y0,
25012                            const int x1, const int y1,
25013                            const int x2, const int y2,
25014                            const tc *const color, const float opacity,
25015                            const unsigned int pattern) {
25016       if (!color)
25017         throw CImgArgumentException(_cimg_instance
25018                                     "draw_triangle : Specified color is (null).",
25019                                     cimg_instance);
25020 
25021       if (is_empty()) return *this;
25022       draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
25023         draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
25024         draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
25025       return *this;
25026     }
25027 
25028     //! Draw a 2d filled colored triangle, with z-buffering.
25029     template<typename tc>
25030     CImg<T>& draw_triangle(CImg<floatT>& zbuffer,
25031                            const int x0, const int y0, const float z0,
25032                            const int x1, const int y1, const float z1,
25033                            const int x2, const int y2, const float z2,
25034                            const tc *const color, const float opacity=1,
25035                            const float brightness=1) {
25036       if (!color)
25037         throw CImgArgumentException(_cimg_instance
25038                                     "draw_triangle() : Specified color is (null).",
25039                                     cimg_instance);
25040       if (!is_sameXY(zbuffer))
25041         throw CImgArgumentException(_cimg_instance
25042                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
25043                                     cimg_instance,
25044                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
25045 
25046       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
25047       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
25048       const float
25049         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
25050         nbrightness = brightness<0?0:(brightness>2?2:brightness);
25051       const int whd = _width*_height*_depth, offx = _spectrum*whd;
25052       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
25053       float nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
25054       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
25055       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2);
25056       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2);
25057       if (ny0>=height() || ny2<0) return *this;
25058       float
25059         pzl = (nz1 - nz0)/(ny1 - ny0),
25060         pzr = (nz2 - nz0)/(ny2 - ny0),
25061         pzn = (nz2 - nz1)/(ny2 - ny1),
25062         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
25063         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
25064       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
25065         if (y==ny1) { zl = nz1; pzl = pzn; }
25066         int xleft = xleft0, xright = xright0;
25067         float zleft = zl, zright = zr;
25068         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright);
25069         const int dx = xright - xleft;
25070         const float pentez = (zright - zleft)/dx;
25071         if (xleft<0 && dx) zleft-=xleft*(zright - zleft)/dx;
25072         if (xleft<0) xleft = 0;
25073         if (xright>=width()-1) xright = width()-1;
25074         T* ptrd = data(xleft,y,0,0);
25075         float *ptrz = zbuffer.data(xleft,y);
25076         if (opacity>=1) {
25077           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25078             if (zleft>=*ptrz) {
25079               *ptrz = zleft;
25080               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
25081               ptrd-=offx;
25082             }
25083             zleft+=pentez;
25084           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25085             if (zleft>=*ptrz) {
25086               *ptrz = zleft;
25087               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whd; }
25088               ptrd-=offx;
25089             }
25090             zleft+=pentez;
25091           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25092             if (zleft>=*ptrz) {
25093               *ptrz = zleft;
25094               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); ptrd+=whd; }
25095               ptrd-=offx;
25096             }
25097             zleft+=pentez;
25098           }
25099         } else {
25100           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25101             if (zleft>=*ptrz) {
25102               *ptrz = zleft;
25103               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whd; }
25104               ptrd-=offx;
25105             }
25106             zleft+=pentez;
25107           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25108             if (zleft>=*ptrz) {
25109               *ptrz = zleft;
25110               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; }
25111               ptrd-=offx;
25112             }
25113             zleft+=pentez;
25114           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25115             if (zleft>=*ptrz) {
25116               *ptrz = zleft;
25117               const tc *col = color;
25118               cimg_forC(*this,c) {
25119                 const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
25120                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
25121                 ptrd+=whd;
25122               }
25123               ptrd-=offx;
25124             }
25125             zleft+=pentez;
25126           }
25127         }
25128         zr+=pzr; zl+=pzl;
25129       }
25130       return *this;
25131     }
25132 
25133     //! Draw a 2d Gouraud-shaded colored triangle.
25134     /**
25135        \param x0 = X-coordinate of the first corner in the instance image.
25136        \param y0 = Y-coordinate of the first corner in the instance image.
25137        \param x1 = X-coordinate of the second corner in the instance image.
25138        \param y1 = Y-coordinate of the second corner in the instance image.
25139        \param x2 = X-coordinate of the third corner in the instance image.
25140        \param y2 = Y-coordinate of the third corner in the instance image.
25141        \param color = array of spectrum() values of type \c T, defining the global drawing color.
25142        \param brightness0 = brightness of the first corner (in [0,2]).
25143        \param brightness1 = brightness of the second corner (in [0,2]).
25144        \param brightness2 = brightness of the third corner (in [0,2]).
25145        \param opacity = opacity of the drawing.
25146        \note Clipping is supported.
25147     **/
25148     template<typename tc>
25149     CImg<T>& draw_triangle(const int x0, const int y0,
25150                            const int x1, const int y1,
25151                            const int x2, const int y2,
25152                            const tc *const color,
25153                            const float brightness0,
25154                            const float brightness1,
25155                            const float brightness2,
25156                            const float opacity=1) {
25157       if (!color)
25158         throw CImgArgumentException(_cimg_instance
25159                                     "draw_triangle : Specified color is (null).",
25160                                     cimg_instance);
25161 
25162       if (is_empty()) return *this;
25163       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
25164       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
25165       const int whd = _width*_height*_depth, offx = _spectrum*whd-1;
25166       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
25167         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
25168         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
25169         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
25170       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1);
25171       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2);
25172       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2);
25173       if (ny0>=height() || ny2<0) return *this;
25174       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
25175         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
25176         if (xright<xleft) cimg::swap(xleft,xright,cleft,cright);
25177         const int
25178           dx = xright - xleft,
25179           dc = cright>cleft?cright - cleft:cleft - cright,
25180           rc = dx?(cright - cleft)/dx:0,
25181           sc = cright>cleft?1:-1,
25182           ndc = dc-(dx?dx*(dc/dx):0);
25183         int errc = dx>>1;
25184         if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx;
25185         if (xleft<0) xleft = 0;
25186         if (xright>=width()-1) xright = width()-1;
25187         T* ptrd = data(xleft,y);
25188         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
25189           const tc *col = color;
25190           cimg_forC(*this,c) {
25191             *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
25192             ptrd+=whd;
25193           }
25194           ptrd-=offx;
25195           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
25196         } else for (int x = xleft; x<=xright; ++x) {
25197           const tc *col = color;
25198           cimg_forC(*this,c) {
25199             const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
25200             *ptrd = (T)(nopacity*val + *ptrd*copacity);
25201             ptrd+=whd;
25202           }
25203           ptrd-=offx;
25204           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
25205         }
25206       }
25207       return *this;
25208     }
25209 
25210     //! Draw a 2d Gouraud-shaded colored triangle, with z-buffering.
25211     template<typename tc>
25212     CImg<T>& draw_triangle(CImg<floatT>& zbuffer,
25213                            const int x0, const int y0, const float z0,
25214                            const int x1, const int y1, const float z1,
25215                            const int x2, const int y2, const float z2,
25216                            const tc *const color,
25217                            const float brightness0,
25218                            const float brightness1,
25219                            const float brightness2,
25220                            const float opacity=1) {
25221       if (!color)
25222         throw CImgArgumentException(_cimg_instance
25223                                     "draw_triangle() : Specified color is (null).",
25224                                     cimg_instance);
25225       if (!is_sameXY(zbuffer))
25226         throw CImgArgumentException(_cimg_instance
25227                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
25228                                     cimg_instance,
25229                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
25230 
25231       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
25232       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
25233       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
25234       const int whd = _width*_height*_depth, offx = _spectrum*whd;
25235       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
25236         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
25237         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
25238         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
25239       float nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
25240       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1);
25241       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2);
25242       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2);
25243       if (ny0>=height() || ny2<0) return *this;
25244       float
25245         pzl = (nz1 - nz0)/(ny1 - ny0),
25246         pzr = (nz2 - nz0)/(ny2 - ny0),
25247         pzn = (nz2 - nz1)/(ny2 - ny1),
25248         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
25249         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
25250       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
25251         if (y==ny1) { zl = nz1; pzl = pzn; }
25252         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
25253         float zleft = zl, zright = zr;
25254         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,cleft,cright);
25255         const int
25256           dx = xright - xleft,
25257           dc = cright>cleft?cright - cleft:cleft - cright,
25258           rc = dx?(cright-cleft)/dx:0,
25259           sc = cright>cleft?1:-1,
25260           ndc = dc-(dx?dx*(dc/dx):0);
25261         const float pentez = (zright - zleft)/dx;
25262         int errc = dx>>1;
25263         if (xleft<0 && dx) {
25264           cleft-=xleft*(cright - cleft)/dx;
25265           zleft-=xleft*(zright - zleft)/dx;
25266         }
25267         if (xleft<0) xleft = 0;
25268         if (xright>=width()-1) xright = width()-1;
25269         T *ptrd = data(xleft,y);
25270         float *ptrz = zbuffer.data(xleft,y);
25271         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
25272           if (zleft>=*ptrz) {
25273             *ptrz = zleft;
25274             const tc *col = color;
25275             cimg_forC(*this,c) {
25276               *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
25277               ptrd+=whd;
25278             }
25279             ptrd-=offx;
25280           }
25281           zleft+=pentez;
25282           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
25283         } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
25284           if (zleft>=*ptrz) {
25285             *ptrz = zleft;
25286             const tc *col = color;
25287             cimg_forC(*this,c) {
25288               const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
25289               *ptrd = (T)(nopacity*val + *ptrd*copacity);
25290               ptrd+=whd;
25291             }
25292             ptrd-=offx;
25293           }
25294           zleft+=pentez;
25295           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
25296         }
25297         zr+=pzr; zl+=pzl;
25298       }
25299       return *this;
25300     }
25301 
25302     //! Draw a colored triangle with interpolated colors.
25303     template<typename tc1, typename tc2, typename tc3>
25304     CImg<T>& draw_triangle(const int x0, const int y0,
25305                            const int x1, const int y1,
25306                            const int x2, const int y2,
25307                            const tc1 *const color1,
25308                            const tc2 *const color2,
25309                            const tc3 *const color3,
25310                            const float opacity=1) {
25311       const unsigned char one = 1;
25312       cimg_forC(*this,c) get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity);
25313       return *this;
25314     }
25315 
25316     //! Draw a 2d textured triangle.
25317     /**
25318        \param x0 = X-coordinate of the first corner in the instance image.
25319        \param y0 = Y-coordinate of the first corner in the instance image.
25320        \param x1 = X-coordinate of the second corner in the instance image.
25321        \param y1 = Y-coordinate of the second corner in the instance image.
25322        \param x2 = X-coordinate of the third corner in the instance image.
25323        \param y2 = Y-coordinate of the third corner in the instance image.
25324        \param texture = texture image used to fill the triangle.
25325        \param tx0 = X-coordinate of the first corner in the texture image.
25326        \param ty0 = Y-coordinate of the first corner in the texture image.
25327        \param tx1 = X-coordinate of the second corner in the texture image.
25328        \param ty1 = Y-coordinate of the second corner in the texture image.
25329        \param tx2 = X-coordinate of the third corner in the texture image.
25330        \param ty2 = Y-coordinate of the third corner in the texture image.
25331        \param opacity = opacity of the drawing.
25332        \param brightness = brightness of the drawing (in [0,2]).
25333        \note Clipping is supported, but texture coordinates do not support clipping.
25334     **/
25335     template<typename tc>
25336     CImg<T>& draw_triangle(const int x0, const int y0,
25337                            const int x1, const int y1,
25338                            const int x2, const int y2,
25339                            const CImg<tc>& texture,
25340                            const int tx0, const int ty0,
25341                            const int tx1, const int ty1,
25342                            const int tx2, const int ty2,
25343                            const float opacity=1,
25344                            const float brightness=1) {
25345       if (texture._depth>1 || texture._spectrum<_spectrum)
25346         throw CImgArgumentException(_cimg_instance
25347                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
25348                                     cimg_instance,
25349                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
25350 
25351       if (is_empty()) return *this;
25352       if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
25353       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
25354       const float
25355         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
25356         nbrightness = brightness<0?0:(brightness>2?2:brightness);
25357       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
25358       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
25359         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2;
25360       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1);
25361       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2);
25362       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2);
25363       if (ny0>=height() || ny2<0) return *this;
25364       _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y,
25365                           nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) {
25366         int
25367           xleft = xleft0, xright = xright0,
25368           txleft = txleft0, txright = txright0,
25369           tyleft = tyleft0, tyright = tyright0;
25370         if (xright<xleft) cimg::swap(xleft,xright,txleft,txright,tyleft,tyright);
25371         const int
25372           dx = xright - xleft,
25373           dtx = txright>txleft?txright - txleft:txleft - txright,
25374           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
25375           rtx = dx?(txright - txleft)/dx:0,
25376           rty = dx?(tyright - tyleft)/dx:0,
25377           stx = txright>txleft?1:-1,
25378           sty = tyright>tyleft?1:-1,
25379           ndtx = dtx - (dx?dx*(dtx/dx):0),
25380           ndty = dty - (dx?dx*(dty/dx):0);
25381         int errtx = dx>>1, errty = errtx;
25382         if (xleft<0 && dx) {
25383           txleft-=xleft*(txright - txleft)/dx;
25384           tyleft-=xleft*(tyright - tyleft)/dx;
25385         }
25386         if (xleft<0) xleft = 0;
25387         if (xright>=width()-1) xright = width()-1;
25388         T* ptrd = data(xleft,y,0,0);
25389         if (opacity>=1) {
25390           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
25391             const tc *col = texture.data(txleft,tyleft);
25392             cimg_forC(*this,c) {
25393               *ptrd = (T)*col;
25394               ptrd+=whd; col+=twhd;
25395             }
25396             ptrd-=offx;
25397             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
25398             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
25399           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
25400             const tc *col = texture.data(txleft,tyleft);
25401             cimg_forC(*this,c) {
25402               *ptrd = (T)(nbrightness**col);
25403               ptrd+=whd; col+=twhd;
25404             }
25405             ptrd-=offx;
25406             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
25407             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
25408           } else for (int x = xleft; x<=xright; ++x) {
25409             const tc *col = texture.data(txleft,tyleft);
25410             cimg_forC(*this,c) {
25411               *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
25412               ptrd+=whd; col+=twhd;
25413             }
25414             ptrd-=offx;
25415             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
25416             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
25417           }
25418         } else {
25419           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
25420             const tc *col = texture.data(txleft,tyleft);
25421             cimg_forC(*this,c) {
25422               *ptrd = (T)(nopacity**col + *ptrd*copacity);
25423               ptrd+=whd; col+=twhd;
25424             }
25425             ptrd-=offx;
25426             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
25427             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
25428           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
25429             const tc *col = texture.data(txleft,tyleft);
25430             cimg_forC(*this,c) {
25431               *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
25432               ptrd+=whd; col+=twhd;
25433             }
25434             ptrd-=offx;
25435             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
25436             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
25437           } else for (int x = xleft; x<=xright; ++x) {
25438             const tc *col = texture.data(txleft,tyleft);
25439             cimg_forC(*this,c) {
25440               const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
25441               *ptrd = (T)(nopacity*val + *ptrd*copacity);
25442               ptrd+=whd; col+=twhd;
25443             }
25444             ptrd-=offx;
25445             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
25446             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
25447           }
25448         }
25449       }
25450       return *this;
25451     }
25452 
25453     //! Draw a 2d textured triangle, with perspective correction.
25454     template<typename tc>
25455     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
25456                            const int x1, const int y1, const float z1,
25457                            const int x2, const int y2, const float z2,
25458                            const CImg<tc>& texture,
25459                            const int tx0, const int ty0,
25460                            const int tx1, const int ty1,
25461                            const int tx2, const int ty2,
25462                            const float opacity=1,
25463                            const float brightness=1) {
25464       if (texture._depth>1 || texture._spectrum<_spectrum)
25465         throw CImgArgumentException(_cimg_instance
25466                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
25467                                     cimg_instance,
25468                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
25469 
25470       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
25471       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
25472       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
25473       const float
25474         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
25475         nbrightness = brightness<0?0:(brightness>2?2:brightness);
25476       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
25477       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
25478       float
25479         ntx0 = tx0/z0, nty0 = ty0/z0,
25480         ntx1 = tx1/z1, nty1 = ty1/z1,
25481         ntx2 = tx2/z2, nty2 = ty2/z2,
25482         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
25483       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
25484       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
25485       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
25486       if (ny0>=height() || ny2<0) return *this;
25487       float
25488         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
25489         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
25490         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
25491         ptyl = (nty1 - nty0)/(ny1 - ny0),
25492         ptyr = (nty2 - nty0)/(ny2 - ny0),
25493         ptyn = (nty2 - nty1)/(ny2 - ny1),
25494         pzl = (nz1 - nz0)/(ny1 - ny0),
25495         pzr = (nz2 - nz0)/(ny2 - ny0),
25496         pzn = (nz2 - nz1)/(ny2 - ny1),
25497         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
25498         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
25499         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
25500         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
25501         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
25502         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
25503       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
25504         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
25505         int xleft = xleft0, xright = xright0;
25506         float
25507           zleft = zl, zright = zr,
25508           txleft = txl, txright = txr,
25509           tyleft = tyl, tyright = tyr;
25510         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
25511         const int dx = xright - xleft;
25512         const float
25513           pentez = (zright - zleft)/dx,
25514           pentetx = (txright - txleft)/dx,
25515           pentety = (tyright - tyleft)/dx;
25516         if (xleft<0 && dx) {
25517           zleft-=xleft*(zright - zleft)/dx;
25518           txleft-=xleft*(txright - txleft)/dx;
25519           tyleft-=xleft*(tyright - tyleft)/dx;
25520         }
25521         if (xleft<0) xleft = 0;
25522         if (xright>=width()-1) xright = width()-1;
25523         T* ptrd = data(xleft,y,0,0);
25524         if (opacity>=1) {
25525           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
25526             const float invz = 1/zleft;
25527             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25528             cimg_forC(*this,c) {
25529               *ptrd = (T)*col;
25530               ptrd+=whd; col+=twhd;
25531             }
25532             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25533           } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) {
25534             const float invz = 1/zleft;
25535             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25536             cimg_forC(*this,c) {
25537               *ptrd = (T)(nbrightness**col);
25538               ptrd+=whd; col+=twhd;
25539             }
25540             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25541           } else for (int x = xleft; x<=xright; ++x) {
25542             const float invz = 1/zleft;
25543             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25544             cimg_forC(*this,c) {
25545               *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
25546               ptrd+=whd; col+=twhd;
25547             }
25548             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25549           }
25550         } else {
25551           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
25552             const float invz = 1/zleft;
25553             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25554             cimg_forC(*this,c) {
25555               *ptrd = (T)(nopacity**col + *ptrd*copacity);
25556               ptrd+=whd; col+=twhd;
25557             }
25558             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25559           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
25560             const float invz = 1/zleft;
25561             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25562             cimg_forC(*this,c) {
25563               *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
25564               ptrd+=whd; col+=twhd;
25565             }
25566             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25567           } else for (int x = xleft; x<=xright; ++x) {
25568             const float invz = 1/zleft;
25569             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25570             cimg_forC(*this,c) {
25571               const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
25572               *ptrd = (T)(nopacity*val + *ptrd*copacity);
25573               ptrd+=whd; col+=twhd;
25574             }
25575             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25576           }
25577         }
25578         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
25579       }
25580       return *this;
25581     }
25582 
25583     //! Draw a 2d textured triangle, with z-buffering and perspective correction.
25584     template<typename tc>
25585     CImg<T>& draw_triangle(CImg<floatT>& zbuffer,
25586                            const int x0, const int y0, const float z0,
25587                            const int x1, const int y1, const float z1,
25588                            const int x2, const int y2, const float z2,
25589                            const CImg<tc>& texture,
25590                            const int tx0, const int ty0,
25591                            const int tx1, const int ty1,
25592                            const int tx2, const int ty2,
25593                            const float opacity=1,
25594                            const float brightness=1) {
25595       if (!is_sameXY(zbuffer))
25596         throw CImgArgumentException(_cimg_instance
25597                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
25598                                     cimg_instance,
25599                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
25600 
25601       if (texture._depth>1 || texture._spectrum<_spectrum)
25602         throw CImgArgumentException(_cimg_instance
25603                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
25604                                     cimg_instance,
25605                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
25606 
25607       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
25608       if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
25609       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
25610       const float
25611         nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
25612         nbrightness = brightness<0?0:(brightness>2?2:brightness);
25613       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd;
25614       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
25615       float
25616         ntx0 = tx0/z0, nty0 = ty0/z0,
25617         ntx1 = tx1/z1, nty1 = ty1/z1,
25618         ntx2 = tx2/z2, nty2 = ty2/z2,
25619         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
25620       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
25621       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
25622       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
25623       if (ny0>=height() || ny2<0) return *this;
25624       float
25625         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
25626         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
25627         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
25628         ptyl = (nty1 - nty0)/(ny1 - ny0),
25629         ptyr = (nty2 - nty0)/(ny2 - ny0),
25630         ptyn = (nty2 - nty1)/(ny2 - ny1),
25631         pzl = (nz1 - nz0)/(ny1 - ny0),
25632         pzr = (nz2 - nz0)/(ny2 - ny0),
25633         pzn = (nz2 - nz1)/(ny2 - ny1),
25634         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
25635         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
25636         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
25637         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
25638         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
25639         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
25640       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
25641         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
25642         int xleft = xleft0, xright = xright0;
25643         float
25644           zleft = zl, zright = zr,
25645           txleft = txl, txright = txr,
25646           tyleft = tyl, tyright = tyr;
25647         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
25648         const int dx = xright - xleft;
25649         const float
25650           pentez = (zright - zleft)/dx,
25651           pentetx = (txright - txleft)/dx,
25652           pentety = (tyright - tyleft)/dx;
25653         if (xleft<0 && dx) {
25654           zleft-=xleft*(zright - zleft)/dx;
25655           txleft-=xleft*(txright - txleft)/dx;
25656           tyleft-=xleft*(tyright - tyleft)/dx;
25657         }
25658         if (xleft<0) xleft = 0;
25659         if (xright>=width()-1) xright = width()-1;
25660         T *ptrd = data(xleft,y,0,0);
25661         float *ptrz = zbuffer.data(xleft,y);
25662         if (opacity>=1) {
25663           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25664             if (zleft>=*ptrz) {
25665               *ptrz = zleft;
25666               const float invz = 1/zleft;
25667               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25668               cimg_forC(*this,c) {
25669                 *ptrd = (T)*col;
25670                 ptrd+=whd; col+=twhd;
25671               }
25672               ptrd-=offx;
25673             }
25674             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25675           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25676             if (zleft>=*ptrz) {
25677               *ptrz = zleft;
25678               const float invz = 1/zleft;
25679               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25680               cimg_forC(*this,c) {
25681                 *ptrd = (T)(nbrightness**col);
25682                 ptrd+=whd; col+=twhd;
25683               }
25684               ptrd-=offx;
25685             }
25686             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25687           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25688             if (zleft>=*ptrz) {
25689               *ptrz = zleft;
25690               const float invz = 1/zleft;
25691               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25692               cimg_forC(*this,c) {
25693                 *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
25694                 ptrd+=whd; col+=twhd;
25695               }
25696               ptrd-=offx;
25697             }
25698             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25699           }
25700         } else {
25701           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25702             if (zleft>=*ptrz) {
25703               *ptrz = zleft;
25704               const float invz = 1/zleft;
25705               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25706               cimg_forC(*this,c) {
25707                 *ptrd = (T)(nopacity**col + *ptrd*copacity);
25708                 ptrd+=whd; col+=twhd;
25709               }
25710               ptrd-=offx;
25711             }
25712             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25713           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25714             if (zleft>=*ptrz) {
25715               *ptrz = zleft;
25716               const float invz = 1/zleft;
25717               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25718               cimg_forC(*this,c) {
25719                 *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
25720                 ptrd+=whd; col+=twhd;
25721               }
25722               ptrd-=offx;
25723             }
25724             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25725           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25726             if (zleft>=*ptrz) {
25727               *ptrz = zleft;
25728               const float invz = 1/zleft;
25729               const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
25730               cimg_forC(*this,c) {
25731                 const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
25732                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
25733                 ptrd+=whd; col+=twhd;
25734               }
25735               ptrd-=offx;
25736             }
25737             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
25738           }
25739         }
25740         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
25741       }
25742       return *this;
25743     }
25744 
25745     //! Draw a 2d Pseudo-Phong-shaded triangle.
25746     /**
25747        \param x0 = X-coordinate of the first corner in the instance image.
25748        \param y0 = Y-coordinate of the first corner in the instance image.
25749        \param x1 = X-coordinate of the second corner in the instance image.
25750        \param y1 = Y-coordinate of the second corner in the instance image.
25751        \param x2 = X-coordinate of the third corner in the instance image.
25752        \param y2 = Y-coordinate of the third corner in the instance image.
25753        \param color = array of spectrum() values of type \c T, defining the global drawing color.
25754        \param light = light image.
25755        \param lx0 = X-coordinate of the first corner in the light image.
25756        \param ly0 = Y-coordinate of the first corner in the light image.
25757        \param lx1 = X-coordinate of the second corner in the light image.
25758        \param ly1 = Y-coordinate of the second corner in the light image.
25759        \param lx2 = X-coordinate of the third corner in the light image.
25760        \param ly2 = Y-coordinate of the third corner in the light image.
25761        \param opacity = opacity of the drawing.
25762        \note Clipping is supported, but texture coordinates do not support clipping.
25763     **/
25764     template<typename tc, typename tl>
25765     CImg<T>& draw_triangle(const int x0, const int y0,
25766                            const int x1, const int y1,
25767                            const int x2, const int y2,
25768                            const tc *const color,
25769                            const CImg<tl>& light,
25770                            const int lx0, const int ly0,
25771                            const int lx1, const int ly1,
25772                            const int lx2, const int ly2,
25773                            const float opacity=1) {
25774       if (!color)
25775         throw CImgArgumentException(_cimg_instance
25776                                     "draw_triangle : Specified color is (null).",
25777                                     cimg_instance);
25778       if (light._depth>1 || light._spectrum<_spectrum)
25779         throw CImgArgumentException(_cimg_instance
25780                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
25781                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
25782 
25783       if (is_empty()) return *this;
25784       if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
25785       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
25786       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
25787       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
25788         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
25789       const int whd = _width*_height*_depth, offx = _spectrum*whd-1;
25790       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1);
25791       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2);
25792       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2);
25793       if (ny0>=height() || ny2<0) return *this;
25794       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
25795                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
25796         int
25797           xleft = xleft0, xright = xright0,
25798           lxleft = lxleft0, lxright = lxright0,
25799           lyleft = lyleft0, lyright = lyright0;
25800         if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright);
25801         const int
25802           dx = xright - xleft,
25803           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
25804           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
25805           rlx = dx?(lxright - lxleft)/dx:0,
25806           rly = dx?(lyright - lyleft)/dx:0,
25807           slx = lxright>lxleft?1:-1,
25808           sly = lyright>lyleft?1:-1,
25809           ndlx = dlx - (dx?dx*(dlx/dx):0),
25810           ndly = dly - (dx?dx*(dly/dx):0);
25811         int errlx = dx>>1, errly = errlx;
25812         if (xleft<0 && dx) {
25813           lxleft-=xleft*(lxright - lxleft)/dx;
25814           lyleft-=xleft*(lyright - lyleft)/dx;
25815         }
25816         if (xleft<0) xleft = 0;
25817         if (xright>=width()-1) xright = width()-1;
25818         T* ptrd = data(xleft,y,0,0);
25819         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
25820           const tc *col = color;
25821           cimg_forC(*this,c) {
25822             const tl l = light(lxleft,lyleft,c);
25823             *ptrd = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
25824             ptrd+=whd;
25825           }
25826           ptrd-=offx;
25827           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
25828           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
25829         } else  for (int x = xleft; x<=xright; ++x) {
25830           const tc *col = color;
25831           cimg_forC(*this,c) {
25832             const tl l = light(lxleft,lyleft,c);
25833             const T val = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
25834             *ptrd = (T)(nopacity*val + *ptrd*copacity);
25835             ptrd+=whd;
25836           }
25837           ptrd-=offx;
25838           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
25839           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
25840         }
25841       }
25842       return *this;
25843     }
25844 
25845     //! Draw a 2d Pseudo-Phong-shaded triangle, with z-buffering.
25846     template<typename tc, typename tl>
25847     CImg<T>& draw_triangle(CImg<floatT>& zbuffer,
25848                            const int x0, const int y0, const float z0,
25849                            const int x1, const int y1, const float z1,
25850                            const int x2, const int y2, const float z2,
25851                            const tc *const color,
25852                            const CImg<tl>& light,
25853                            const int lx0, const int ly0,
25854                            const int lx1, const int ly1,
25855                            const int lx2, const int ly2,
25856                            const float opacity=1) {
25857       if (!color)
25858         throw CImgArgumentException(_cimg_instance
25859                                     "draw_triangle() : Specified color is (null).",
25860                                     cimg_instance);
25861       if (light._depth>1 || light._spectrum<_spectrum)
25862         throw CImgArgumentException(_cimg_instance
25863                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
25864                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
25865       if (!is_sameXY(zbuffer))
25866         throw CImgArgumentException(_cimg_instance
25867                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
25868                                     cimg_instance,
25869                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
25870 
25871       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
25872       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
25873                                                      +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
25874       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
25875       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
25876       const int whd = _width*_height*_depth, offx = _spectrum*whd;
25877       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
25878         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
25879       float nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
25880       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1);
25881       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2);
25882       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2);
25883       if (ny0>=height() || ny2<0) return *this;
25884       float
25885         pzl = (nz1 - nz0)/(ny1 - ny0),
25886         pzr = (nz2 - nz0)/(ny2 - ny0),
25887         pzn = (nz2 - nz1)/(ny2 - ny1),
25888         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
25889         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
25890       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
25891                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
25892         if (y==ny1) { zl = nz1; pzl = pzn; }
25893         int
25894           xleft = xleft0, xright = xright0,
25895           lxleft = lxleft0, lxright = lxright0,
25896           lyleft = lyleft0, lyright = lyright0;
25897         float zleft = zl, zright = zr;
25898         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,lxleft,lxright,lyleft,lyright);
25899         const int
25900           dx = xright - xleft,
25901           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
25902           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
25903           rlx = dx?(lxright - lxleft)/dx:0,
25904           rly = dx?(lyright - lyleft)/dx:0,
25905           slx = lxright>lxleft?1:-1,
25906           sly = lyright>lyleft?1:-1,
25907           ndlx = dlx - (dx?dx*(dlx/dx):0),
25908           ndly = dly - (dx?dx*(dly/dx):0);
25909         const float pentez = (zright - zleft)/dx;
25910         int errlx = dx>>1, errly = errlx;
25911         if (xleft<0 && dx) {
25912           zleft-=xleft*(zright - zleft)/dx;
25913           lxleft-=xleft*(lxright - lxleft)/dx;
25914           lyleft-=xleft*(lyright - lyleft)/dx;
25915         }
25916         if (xleft<0) xleft = 0;
25917         if (xright>=width()-1) xright = width()-1;
25918         T *ptrd = data(xleft,y,0,0);
25919         float *ptrz = zbuffer.data(xleft,y);
25920         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25921           if (zleft>=*ptrz) {
25922             *ptrz = zleft;
25923             const tc *col = color;
25924             cimg_forC(*this,c) {
25925               const tl l = light(lxleft,lyleft,c);
25926               const tc cval = *(col++);
25927               *ptrd = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
25928               ptrd+=whd;
25929             }
25930             ptrd-=offx;
25931           }
25932           zleft+=pentez;
25933           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
25934           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
25935         } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
25936           if (zleft>=*ptrz) {
25937             *ptrz = zleft;
25938             const tc *col = color;
25939             cimg_forC(*this,c) {
25940               const tl l = light(lxleft,lyleft,c);
25941               const tc cval = *(col++);
25942               const T val = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
25943               *ptrd = (T)(nopacity*val + *ptrd*copacity);
25944               ptrd+=whd;
25945             }
25946             ptrd-=offx;
25947           }
25948           zleft+=pentez;
25949           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
25950           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
25951         }
25952         zr+=pzr; zl+=pzl;
25953       }
25954       return *this;
25955     }
25956 
25957     //! Draw a 2d Gouraud-shaded textured triangle.
25958     /**
25959        \param x0 = X-coordinate of the first corner in the instance image.
25960        \param y0 = Y-coordinate of the first corner in the instance image.
25961        \param x1 = X-coordinate of the second corner in the instance image.
25962        \param y1 = Y-coordinate of the second corner in the instance image.
25963        \param x2 = X-coordinate of the third corner in the instance image.
25964        \param y2 = Y-coordinate of the third corner in the instance image.
25965        \param texture = texture image used to fill the triangle.
25966        \param tx0 = X-coordinate of the first corner in the texture image.
25967        \param ty0 = Y-coordinate of the first corner in the texture image.
25968        \param tx1 = X-coordinate of the second corner in the texture image.
25969        \param ty1 = Y-coordinate of the second corner in the texture image.
25970        \param tx2 = X-coordinate of the third corner in the texture image.
25971        \param ty2 = Y-coordinate of the third corner in the texture image.
25972        \param brightness0 = brightness value of the first corner.
25973        \param brightness1 = brightness value of the second corner.
25974        \param brightness2 = brightness value of the third corner.
25975        \param opacity = opacity of the drawing.
25976        \note Clipping is supported, but texture coordinates do not support clipping.
25977     **/
25978     template<typename tc>
25979     CImg<T>& draw_triangle(const int x0, const int y0,
25980                            const int x1, const int y1,
25981                            const int x2, const int y2,
25982                            const CImg<tc>& texture,
25983                            const int tx0, const int ty0,
25984                            const int tx1, const int ty1,
25985                            const int tx2, const int ty2,
25986                            const float brightness0,
25987                            const float brightness1,
25988                            const float brightness2,
25989                            const float opacity=1) {
25990       if (texture._depth>1 || texture._spectrum<_spectrum)
25991         throw CImgArgumentException(_cimg_instance
25992                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
25993                                     cimg_instance,
25994                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
25995 
25996       if (is_empty()) return *this;
25997       if (is_overlapped(texture))
25998         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,brightness0,brightness1,brightness2,opacity);
25999       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
26000       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26001       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
26002       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
26003         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
26004         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
26005         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
26006         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
26007       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1);
26008       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2);
26009       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2);
26010       if (ny0>=height() || ny2<0) return *this;
26011       _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y,
26012                           nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) {
26013         int
26014           xleft = xleft0, xright = xright0,
26015           cleft = cleft0, cright = cright0,
26016           txleft = txleft0, txright = txright0,
26017           tyleft = tyleft0, tyright = tyright0;
26018         if (xright<xleft) cimg::swap(xleft,xright,cleft,cright,txleft,txright,tyleft,tyright);
26019         const int
26020           dx = xright - xleft,
26021           dc = cright>cleft?cright - cleft:cleft - cright,
26022           dtx = txright>txleft?txright - txleft:txleft - txright,
26023           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
26024           rc = dx?(cright - cleft)/dx:0,
26025           rtx = dx?(txright - txleft)/dx:0,
26026           rty = dx?(tyright - tyleft)/dx:0,
26027           sc = cright>cleft?1:-1,
26028           stx = txright>txleft?1:-1,
26029           sty = tyright>tyleft?1:-1,
26030           ndc = dc - (dx?dx*(dc/dx):0),
26031           ndtx = dtx - (dx?dx*(dtx/dx):0),
26032           ndty = dty - (dx?dx*(dty/dx):0);
26033         int errc = dx>>1, errtx = errc, errty = errc;
26034         if (xleft<0 && dx) {
26035           cleft-=xleft*(cright - cleft)/dx;
26036           txleft-=xleft*(txright - txleft)/dx;
26037           tyleft-=xleft*(tyright - tyleft)/dx;
26038         }
26039         if (xleft<0) xleft = 0;
26040         if (xright>=width()-1) xright = width()-1;
26041         T* ptrd = data(xleft,y,0,0);
26042         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
26043           const tc *col = texture.data(txleft,tyleft);
26044           cimg_forC(*this,c) {
26045             *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
26046             ptrd+=whd; col+=twhd;
26047           }
26048           ptrd-=offx;
26049           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
26050           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
26051           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
26052         } else for (int x = xleft; x<=xright; ++x) {
26053           const tc *col = texture.data(txleft,tyleft);
26054           cimg_forC(*this,c) {
26055             const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
26056             *ptrd = (T)(nopacity*val + *ptrd*copacity);
26057             ptrd+=whd; col+=twhd;
26058           }
26059           ptrd-=offx;
26060           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
26061           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
26062           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
26063         }
26064       }
26065       return *this;
26066     }
26067 
26068     //! Draw a 2d Gouraud-shaded textured triangle, with perspective correction.
26069     template<typename tc>
26070     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
26071                            const int x1, const int y1, const float z1,
26072                            const int x2, const int y2, const float z2,
26073                            const CImg<tc>& texture,
26074                            const int tx0, const int ty0,
26075                            const int tx1, const int ty1,
26076                            const int tx2, const int ty2,
26077                            const float brightness0,
26078                            const float brightness1,
26079                            const float brightness2,
26080                            const float opacity=1) {
26081       if (texture._depth>1 || texture._spectrum<_spectrum)
26082         throw CImgArgumentException(_cimg_instance
26083                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
26084                                     cimg_instance,
26085                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
26086 
26087       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
26088       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
26089                                                        brightness0,brightness1,brightness2,opacity);
26090       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
26091       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26092       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
26093       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
26094         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
26095         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
26096         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
26097       float
26098         ntx0 = tx0/z0, nty0 = ty0/z0,
26099         ntx1 = tx1/z1, nty1 = ty1/z1,
26100         ntx2 = tx2/z2, nty2 = ty2/z2,
26101         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
26102       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
26103       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
26104       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
26105       if (ny0>=height() || ny2<0) return *this;
26106       float
26107         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
26108         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
26109         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
26110         ptyl = (nty1 - nty0)/(ny1 - ny0),
26111         ptyr = (nty2 - nty0)/(ny2 - ny0),
26112         ptyn = (nty2 - nty1)/(ny2 - ny1),
26113         pzl = (nz1 - nz0)/(ny1 - ny0),
26114         pzr = (nz2 - nz0)/(ny2 - ny0),
26115         pzn = (nz2 - nz1)/(ny2 - ny1),
26116         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
26117         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
26118         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
26119         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
26120         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
26121         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
26122       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
26123         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
26124         int
26125           xleft = xleft0, xright = xright0,
26126           cleft = cleft0, cright = cright0;
26127         float
26128           zleft = zl, zright = zr,
26129           txleft = txl, txright = txr,
26130           tyleft = tyl, tyright = tyr;
26131         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
26132         const int
26133           dx = xright - xleft,
26134           dc = cright>cleft?cright - cleft:cleft - cright,
26135           rc = dx?(cright - cleft)/dx:0,
26136           sc = cright>cleft?1:-1,
26137           ndc = dc - (dx?dx*(dc/dx):0);
26138         const float
26139           pentez = (zright - zleft)/dx,
26140           pentetx = (txright - txleft)/dx,
26141           pentety = (tyright - tyleft)/dx;
26142         int errc = dx>>1;
26143         if (xleft<0 && dx) {
26144           cleft-=xleft*(cright - cleft)/dx;
26145           zleft-=xleft*(zright - zleft)/dx;
26146           txleft-=xleft*(txright - txleft)/dx;
26147           tyleft-=xleft*(tyright - tyleft)/dx;
26148         }
26149         if (xleft<0) xleft = 0;
26150         if (xright>=width()-1) xright = width()-1;
26151         T* ptrd = data(xleft,y,0,0);
26152         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
26153           const float invz = 1/zleft;
26154           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
26155           cimg_forC(*this,c) {
26156             *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
26157             ptrd+=whd; col+=twhd;
26158           }
26159           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
26160           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
26161         } else for (int x = xleft; x<=xright; ++x) {
26162           const float invz = 1/zleft;
26163           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
26164           cimg_forC(*this,c) {
26165             const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
26166             *ptrd = (T)(nopacity*val + *ptrd*copacity);
26167             ptrd+=whd; col+=twhd;
26168           }
26169           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
26170           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
26171         }
26172         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
26173       }
26174       return *this;
26175     }
26176 
26177     //! Draw a 2d Gouraud-shaded textured triangle, with z-buffering and perspective correction.
26178     template<typename tc>
26179     CImg<T>& draw_triangle(CImg<floatT>& zbuffer,
26180                            const int x0, const int y0, const float z0,
26181                            const int x1, const int y1, const float z1,
26182                            const int x2, const int y2, const float z2,
26183                            const CImg<tc>& texture,
26184                            const int tx0, const int ty0,
26185                            const int tx1, const int ty1,
26186                            const int tx2, const int ty2,
26187                            const float brightness0,
26188                            const float brightness1,
26189                            const float brightness2,
26190                            const float opacity=1) {
26191       if (!is_sameXY(zbuffer))
26192         throw CImgArgumentException(_cimg_instance
26193                                     "draw_line() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
26194                                     cimg_instance,
26195                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
26196 
26197       if (texture._depth>1 || texture._spectrum<_spectrum)
26198         throw CImgArgumentException(_cimg_instance
26199                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
26200                                     cimg_instance,
26201                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
26202 
26203       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
26204       if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
26205                                                        brightness0,brightness1,brightness2,opacity);
26206       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
26207       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26208       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd;
26209       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
26210         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
26211         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
26212         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
26213       float
26214         ntx0 = tx0/z0, nty0 = ty0/z0,
26215         ntx1 = tx1/z1, nty1 = ty1/z1,
26216         ntx2 = tx2/z2, nty2 = ty2/z2,
26217         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
26218       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
26219       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
26220       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
26221       if (ny0>=height() || ny2<0) return *this;
26222       float
26223         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
26224         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
26225         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
26226         ptyl = (nty1 - nty0)/(ny1 - ny0),
26227         ptyr = (nty2 - nty0)/(ny2 - ny0),
26228         ptyn = (nty2 - nty1)/(ny2 - ny1),
26229         pzl = (nz1 - nz0)/(ny1 - ny0),
26230         pzr = (nz2 - nz0)/(ny2 - ny0),
26231         pzn = (nz2 - nz1)/(ny2 - ny1),
26232         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
26233         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
26234         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
26235         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
26236         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
26237         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
26238       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
26239         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
26240         int
26241           xleft = xleft0, xright = xright0,
26242           cleft = cleft0, cright = cright0;
26243         float
26244           zleft = zl, zright = zr,
26245           txleft = txl, txright = txr,
26246           tyleft = tyl, tyright = tyr;
26247         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
26248         const int
26249           dx = xright - xleft,
26250           dc = cright>cleft?cright - cleft:cleft - cright,
26251           rc = dx?(cright - cleft)/dx:0,
26252           sc = cright>cleft?1:-1,
26253           ndc = dc - (dx?dx*(dc/dx):0);
26254         const float
26255           pentez = (zright - zleft)/dx,
26256           pentetx = (txright - txleft)/dx,
26257           pentety = (tyright - tyleft)/dx;
26258         int errc = dx>>1;
26259         if (xleft<0 && dx) {
26260           cleft-=xleft*(cright - cleft)/dx;
26261           zleft-=xleft*(zright - zleft)/dx;
26262           txleft-=xleft*(txright - txleft)/dx;
26263           tyleft-=xleft*(tyright - tyleft)/dx;
26264         }
26265         if (xleft<0) xleft = 0;
26266         if (xright>=width()-1) xright = width()-1;
26267         T* ptrd = data(xleft,y);
26268         float *ptrz = zbuffer.data(xleft,y);
26269         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
26270           if (zleft>=*ptrz) {
26271             *ptrz = zleft;
26272             const float invz = 1/zleft;
26273             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
26274             cimg_forC(*this,c) {
26275               *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
26276               ptrd+=whd; col+=twhd;
26277             }
26278             ptrd-=offx;
26279           }
26280           zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
26281           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
26282         } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
26283           if (zleft>=*ptrz) {
26284             *ptrz = zleft;
26285             const float invz = 1/zleft;
26286             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
26287             cimg_forC(*this,c) {
26288               const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
26289               *ptrd = (T)(nopacity*val + *ptrd*copacity);
26290               ptrd+=whd; col+=twhd;
26291             }
26292             ptrd-=offx;
26293           }
26294           zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
26295           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
26296         }
26297         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
26298       }
26299       return *this;
26300     }
26301 
26302     //! Draw a 2d Pseudo-Phong-shaded textured triangle.
26303     /**
26304        \param x0 = X-coordinate of the first corner in the instance image.
26305        \param y0 = Y-coordinate of the first corner in the instance image.
26306        \param x1 = X-coordinate of the second corner in the instance image.
26307        \param y1 = Y-coordinate of the second corner in the instance image.
26308        \param x2 = X-coordinate of the third corner in the instance image.
26309        \param y2 = Y-coordinate of the third corner in the instance image.
26310        \param texture = texture image used to fill the triangle.
26311        \param tx0 = X-coordinate of the first corner in the texture image.
26312        \param ty0 = Y-coordinate of the first corner in the texture image.
26313        \param tx1 = X-coordinate of the second corner in the texture image.
26314        \param ty1 = Y-coordinate of the second corner in the texture image.
26315        \param tx2 = X-coordinate of the third corner in the texture image.
26316        \param ty2 = Y-coordinate of the third corner in the texture image.
26317        \param light = light image.
26318        \param lx0 = X-coordinate of the first corner in the light image.
26319        \param ly0 = Y-coordinate of the first corner in the light image.
26320        \param lx1 = X-coordinate of the second corner in the light image.
26321        \param ly1 = Y-coordinate of the second corner in the light image.
26322        \param lx2 = X-coordinate of the third corner in the light image.
26323        \param ly2 = Y-coordinate of the third corner in the light image.
26324        \param opacity = opacity of the drawing.
26325        \note Clipping is supported, but texture coordinates do not support clipping.
26326     **/
26327     template<typename tc, typename tl>
26328     CImg<T>& draw_triangle(const int x0, const int y0,
26329                            const int x1, const int y1,
26330                            const int x2, const int y2,
26331                            const CImg<tc>& texture,
26332                            const int tx0, const int ty0,
26333                            const int tx1, const int ty1,
26334                            const int tx2, const int ty2,
26335                            const CImg<tl>& light,
26336                            const int lx0, const int ly0,
26337                            const int lx1, const int ly1,
26338                            const int lx2, const int ly2,
26339                            const float opacity=1) {
26340       if (texture._depth>1 || texture._spectrum<_spectrum)
26341         throw CImgArgumentException(_cimg_instance
26342                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
26343                                     cimg_instance,
26344                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
26345       if (light._depth>1 || light._spectrum<_spectrum)
26346         throw CImgArgumentException(_cimg_instance
26347                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
26348                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
26349 
26350       if (is_empty()) return *this;
26351       if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
26352       if (is_overlapped(light))   return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
26353       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
26354       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26355       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
26356       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
26357         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
26358         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
26359       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1);
26360       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2);
26361       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2);
26362       if (ny0>=height() || ny2<0) return *this;
26363       _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y,
26364                           nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) {
26365         int
26366           xleft = xleft0, xright = xright0,
26367           lxleft = lxleft0, lxright = lxright0,
26368           lyleft = lyleft0, lyright = lyright0,
26369           txleft = txleft0, txright = txright0,
26370           tyleft = tyleft0, tyright = tyright0;
26371         if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright,txleft,txright,tyleft,tyright);
26372         const int
26373           dx = xright - xleft,
26374           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
26375           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
26376           dtx = txright>txleft?txright - txleft:txleft - txright,
26377           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
26378           rlx = dx?(lxright - lxleft)/dx:0,
26379           rly = dx?(lyright - lyleft)/dx:0,
26380           rtx = dx?(txright - txleft)/dx:0,
26381           rty = dx?(tyright - tyleft)/dx:0,
26382           slx = lxright>lxleft?1:-1,
26383           sly = lyright>lyleft?1:-1,
26384           stx = txright>txleft?1:-1,
26385           sty = tyright>tyleft?1:-1,
26386           ndlx = dlx - (dx?dx*(dlx/dx):0),
26387           ndly = dly - (dx?dx*(dly/dx):0),
26388           ndtx = dtx - (dx?dx*(dtx/dx):0),
26389           ndty = dty - (dx?dx*(dty/dx):0);
26390         int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx;
26391         if (xleft<0 && dx) {
26392           lxleft-=xleft*(lxright - lxleft)/dx;
26393           lyleft-=xleft*(lyright - lyleft)/dx;
26394           txleft-=xleft*(txright - txleft)/dx;
26395           tyleft-=xleft*(tyright - tyleft)/dx;
26396         }
26397         if (xleft<0) xleft = 0;
26398         if (xright>=width()-1) xright = width()-1;
26399         T* ptrd = data(xleft,y,0,0);
26400         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
26401           const tc *col = texture.data(txleft,tyleft);
26402           cimg_forC(*this,c) {
26403             const tl l = light(lxleft,lyleft,c);
26404             *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
26405             ptrd+=whd; col+=twhd;
26406           }
26407           ptrd-=offx;
26408           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
26409           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
26410           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
26411           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
26412         } else for (int x = xleft; x<=xright; ++x) {
26413           const tc *col = texture.data(txleft,tyleft);
26414           cimg_forC(*this,c) {
26415             const tl l = light(lxleft,lyleft,c);
26416             const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
26417             *ptrd = (T)(nopacity*val + *ptrd*copacity);
26418             ptrd+=whd; col+=twhd;
26419           }
26420           ptrd-=offx;
26421           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
26422           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
26423           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
26424           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
26425         }
26426       }
26427       return *this;
26428     }
26429 
26430     //! Draw a 2d Pseudo-Phong-shaded textured triangle, with perspective correction.
26431     template<typename tc, typename tl>
26432     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
26433                            const int x1, const int y1, const float z1,
26434                            const int x2, const int y2, const float z2,
26435                            const CImg<tc>& texture,
26436                            const int tx0, const int ty0,
26437                            const int tx1, const int ty1,
26438                            const int tx2, const int ty2,
26439                            const CImg<tl>& light,
26440                            const int lx0, const int ly0,
26441                            const int lx1, const int ly1,
26442                            const int lx2, const int ly2,
26443                            const float opacity=1) {
26444       if (texture._depth>1 || texture._spectrum<_spectrum)
26445         throw CImgArgumentException(_cimg_instance
26446                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
26447                                     cimg_instance,
26448                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
26449       if (light._depth>1 || light._spectrum<_spectrum)
26450         throw CImgArgumentException(_cimg_instance
26451                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
26452                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
26453 
26454       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
26455       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
26456       if (is_overlapped(light)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
26457       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
26458       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26459       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
26460       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
26461         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
26462       float
26463         ntx0 = tx0/z0, nty0 = ty0/z0,
26464         ntx1 = tx1/z1, nty1 = ty1/z1,
26465         ntx2 = tx2/z2, nty2 = ty2/z2,
26466         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
26467       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
26468       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
26469       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
26470       if (ny0>=height() || ny2<0) return *this;
26471       float
26472         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
26473         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
26474         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
26475         ptyl = (nty1 - nty0)/(ny1 - ny0),
26476         ptyr = (nty2 - nty0)/(ny2 - ny0),
26477         ptyn = (nty2 - nty1)/(ny2 - ny1),
26478         pzl = (nz1 - nz0)/(ny1 - ny0),
26479         pzr = (nz2 - nz0)/(ny2 - ny0),
26480         pzn = (nz2 - nz1)/(ny2 - ny1),
26481         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
26482         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
26483         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
26484         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
26485         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
26486         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
26487       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
26488                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
26489         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
26490         int
26491           xleft = xleft0, xright = xright0,
26492           lxleft = lxleft0, lxright = lxright0,
26493           lyleft = lyleft0, lyright = lyright0;
26494         float
26495           zleft = zl, zright = zr,
26496           txleft = txl, txright = txr,
26497           tyleft = tyl, tyright = tyr;
26498         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
26499         const int
26500           dx = xright - xleft,
26501           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
26502           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
26503           rlx = dx?(lxright - lxleft)/dx:0,
26504           rly = dx?(lyright - lyleft)/dx:0,
26505           slx = lxright>lxleft?1:-1,
26506           sly = lyright>lyleft?1:-1,
26507           ndlx = dlx - (dx?dx*(dlx/dx):0),
26508           ndly = dly - (dx?dx*(dly/dx):0);
26509         const float
26510           pentez = (zright - zleft)/dx,
26511           pentetx = (txright - txleft)/dx,
26512           pentety = (tyright - tyleft)/dx;
26513         int errlx = dx>>1, errly = errlx;
26514         if (xleft<0 && dx) {
26515           zleft-=xleft*(zright - zleft)/dx;
26516           lxleft-=xleft*(lxright - lxleft)/dx;
26517           lyleft-=xleft*(lyright - lyleft)/dx;
26518           txleft-=xleft*(txright - txleft)/dx;
26519           tyleft-=xleft*(tyright - tyleft)/dx;
26520         }
26521         if (xleft<0) xleft = 0;
26522         if (xright>=width()-1) xright = width()-1;
26523         T* ptrd = data(xleft,y,0,0);
26524         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
26525           const float invz = 1/zleft;
26526           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
26527           cimg_forC(*this,c) {
26528             const tl l = light(lxleft,lyleft,c);
26529             *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
26530             ptrd+=whd; col+=twhd;
26531           }
26532           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
26533           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
26534           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
26535         } else for (int x = xleft; x<=xright; ++x) {
26536           const float invz = 1/zleft;
26537           const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
26538           cimg_forC(*this,c) {
26539             const tl l = light(lxleft,lyleft,c);
26540             const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
26541             *ptrd = (T)(nopacity*val + *ptrd*copacity);
26542             ptrd+=whd; col+=twhd;
26543           }
26544           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
26545           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
26546           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
26547         }
26548         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
26549       }
26550       return *this;
26551     }
26552 
26553     //! Draw a 2d Pseudo-Phong-shaded textured triangle, with z-buffering and perspective correction.
26554     template<typename tc, typename tl>
26555     CImg<T>& draw_triangle(CImg<floatT>& zbuffer,
26556                            const int x0, const int y0, const float z0,
26557                            const int x1, const int y1, const float z1,
26558                            const int x2, const int y2, const float z2,
26559                            const CImg<tc>& texture,
26560                            const int tx0, const int ty0,
26561                            const int tx1, const int ty1,
26562                            const int tx2, const int ty2,
26563                            const CImg<tl>& light,
26564                            const int lx0, const int ly0,
26565                            const int lx1, const int ly1,
26566                            const int lx2, const int ly2,
26567                            const float opacity=1) {
26568       if (!is_sameXY(zbuffer))
26569         throw CImgArgumentException(_cimg_instance
26570                                     "draw_triangle() : Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
26571                                     cimg_instance,
26572                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
26573       if (texture._depth>1 || texture._spectrum<_spectrum)
26574         throw CImgArgumentException(_cimg_instance
26575                                     "draw_triangle() : Invalid specified texture (%u,%u,%u,%u,%p).",
26576                                     cimg_instance,
26577                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
26578       if (light._depth>1 || light._spectrum<_spectrum)
26579         throw CImgArgumentException(_cimg_instance
26580                                     "draw_triangle() : Invalid specified light texture (%u,%u,%u,%u,%p).",
26581                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
26582 
26583       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
26584       if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
26585                                                        +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
26586       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
26587                                                      texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
26588       static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
26589       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26590       const int whd = _width*_height*_depth, twhd = texture._width*texture._height*texture._depth, offx = _spectrum*whd;
26591       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
26592         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
26593       float
26594         ntx0 = tx0/z0, nty0 = ty0/z0,
26595         ntx1 = tx1/z1, nty1 = ty1/z1,
26596         ntx2 = tx2/z2, nty2 = ty2/z2,
26597         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
26598       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
26599       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
26600       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
26601       if (ny0>=height() || ny2<0) return *this;
26602       float
26603         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
26604         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
26605         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
26606         ptyl = (nty1 - nty0)/(ny1 - ny0),
26607         ptyr = (nty2 - nty0)/(ny2 - ny0),
26608         ptyn = (nty2 - nty1)/(ny2 - ny1),
26609         pzl = (nz1 - nz0)/(ny1 - ny0),
26610         pzr = (nz2 - nz0)/(ny2 - ny0),
26611         pzn = (nz2 - nz1)/(ny2 - ny1),
26612         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
26613         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
26614         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
26615         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
26616         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
26617         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
26618       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
26619                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
26620         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
26621         int
26622           xleft = xleft0, xright = xright0,
26623           lxleft = lxleft0, lxright = lxright0,
26624           lyleft = lyleft0, lyright = lyright0;
26625         float
26626           zleft = zl, zright = zr,
26627           txleft = txl, txright = txr,
26628           tyleft = tyl, tyright = tyr;
26629         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
26630         const int
26631           dx = xright - xleft,
26632           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
26633           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
26634           rlx = dx?(lxright - lxleft)/dx:0,
26635           rly = dx?(lyright - lyleft)/dx:0,
26636           slx = lxright>lxleft?1:-1,
26637           sly = lyright>lyleft?1:-1,
26638           ndlx = dlx - (dx?dx*(dlx/dx):0),
26639           ndly = dly - (dx?dx*(dly/dx):0);
26640         const float
26641           pentez = (zright - zleft)/dx,
26642           pentetx = (txright - txleft)/dx,
26643           pentety = (tyright - tyleft)/dx;
26644         int errlx = dx>>1, errly = errlx;
26645         if (xleft<0 && dx) {
26646           zleft-=xleft*(zright - zleft)/dx;
26647           lxleft-=xleft*(lxright - lxleft)/dx;
26648           lyleft-=xleft*(lyright - lyleft)/dx;
26649           txleft-=xleft*(txright - txleft)/dx;
26650           tyleft-=xleft*(tyright - tyleft)/dx;
26651         }
26652         if (xleft<0) xleft = 0;
26653         if (xright>=width()-1) xright = width()-1;
26654         T* ptrd = data(xleft,y);
26655         float *ptrz = zbuffer.data(xleft,y);
26656         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
26657           if (zleft>=*ptrz) {
26658             *ptrz = zleft;
26659             const float invz = 1/zleft;
26660             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
26661             cimg_forC(*this,c) {
26662               const tl l = light(lxleft,lyleft,c);
26663               *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
26664               ptrd+=whd; col+=twhd;
26665             }
26666             ptrd-=offx;
26667           }
26668           zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
26669           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
26670           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
26671         } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
26672           if (zleft>=*ptrz) {
26673             *ptrz = zleft;
26674             const float invz = 1/zleft;
26675             const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
26676             cimg_forC(*this,c) {
26677               const tl l = light(lxleft,lyleft,c);
26678               const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
26679               *ptrd = (T)(nopacity*val + *ptrd*copacity);
26680               ptrd+=whd; col+=twhd;
26681             }
26682             ptrd-=offx;
26683           }
26684           zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
26685           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
26686           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
26687         }
26688         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
26689       }
26690       return *this;
26691     }
26692 
26693     //! Draw a 4d filled rectangle in the instance image, at coordinates (\c x0,\c y0,\c z0,\c c0)-(\c x1,\c y1,\c z1,\c c1).
26694     /**
26695        \param x0 X-coordinate of the upper-left rectangle corner.
26696        \param y0 Y-coordinate of the upper-left rectangle corner.
26697        \param z0 Z-coordinate of the upper-left rectangle corner.
26698        \param c0 C-coordinate of the upper-left rectangle corner.
26699        \param x1 X-coordinate of the lower-right rectangle corner.
26700        \param y1 Y-coordinate of the lower-right rectangle corner.
26701        \param z1 Z-coordinate of the lower-right rectangle corner.
26702        \param c1 C-coordinate of the lower-right rectangle corner.
26703        \param val Scalar value used to fill the rectangle area.
26704        \param opacity Drawing opacity (optional).
26705        \note
26706        - Clipping is supported.
26707     **/
26708     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
26709                             const int x1, const int y1, const int z1, const int c1,
26710                             const T val, const float opacity=1) {
26711       if (is_empty()) return *this;
26712       const bool bx = (x0<x1), by = (y0<y1), bz = (z0<z1), bc = (c0<c1);
26713       const int
26714         nx0 = bx?x0:x1, nx1 = bx?x1:x0,
26715         ny0 = by?y0:y1, ny1 = by?y1:y0,
26716         nz0 = bz?z0:z1, nz1 = bz?z1:z0,
26717         nc0 = bc?c0:c1, nc1 = bc?c1:c0;
26718       const int
26719         lX = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
26720         lY = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
26721         lZ = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
26722         lC = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
26723       const unsigned int offX = _width - lX, offY = _width*(_height - lY), offZ = _width*_height*(_depth - lZ);
26724       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
26725       T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
26726       if (lX>0 && lY>0 && lZ>0 && lC>0)
26727         for (int v = 0; v<lC; ++v) {
26728           for (int z = 0; z<lZ; ++z) {
26729             for (int y = 0; y<lY; ++y) {
26730               if (opacity>=1) {
26731                 if (sizeof(T)!=1) { for (int x = 0; x<lX; ++x) *(ptrd++) = val; ptrd+=offX; }
26732                 else { std::memset(ptrd,(int)val,lX); ptrd+=_width; }
26733               } else { for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
26734             }
26735             ptrd+=offY;
26736           }
26737           ptrd+=offZ;
26738         }
26739       return *this;
26740     }
26741 
26742     //! Draw a 3d filled colored rectangle in the instance image, at coordinates (\c x0,\c y0,\c z0)-(\c x1,\c y1,\c z1).
26743     /**
26744        \param x0 X-coordinate of the upper-left rectangle corner.
26745        \param y0 Y-coordinate of the upper-left rectangle corner.
26746        \param z0 Z-coordinate of the upper-left rectangle corner.
26747        \param x1 X-coordinate of the lower-right rectangle corner.
26748        \param y1 Y-coordinate of the lower-right rectangle corner.
26749        \param z1 Z-coordinate of the lower-right rectangle corner.
26750        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
26751        \param opacity Drawing opacity (optional).
26752        \note
26753        - Clipping is supported.
26754     **/
26755     template<typename tc>
26756     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
26757                             const int x1, const int y1, const int z1,
26758                             const tc *const color, const float opacity=1) {
26759       if (!color)
26760         throw CImgArgumentException(_cimg_instance
26761                                     "draw_rectangle : Specified color is (null).",
26762                                     cimg_instance);
26763 
26764       if (is_empty()) return *this;
26765       cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,color[c],opacity);
26766       return *this;
26767     }
26768 
26769     //! Draw a 3d outlined colored rectangle in the instance image.
26770     template<typename tc>
26771     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
26772                             const int x1, const int y1, const int z1,
26773                             const tc *const color, const float opacity,
26774                             const unsigned int pattern) {
26775       return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true).
26776         draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false).
26777         draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false).
26778         draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false).
26779         draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true).
26780         draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false).
26781         draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false).
26782         draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false).
26783         draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true).
26784         draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true).
26785         draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true).
26786         draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true);
26787     }
26788 
26789     //! Draw a 2d filled colored rectangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1).
26790     /**
26791        \param x0 X-coordinate of the upper-left rectangle corner.
26792        \param y0 Y-coordinate of the upper-left rectangle corner.
26793        \param x1 X-coordinate of the lower-right rectangle corner.
26794        \param y1 Y-coordinate of the lower-right rectangle corner.
26795        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
26796        \param opacity Drawing opacity (optional).
26797        \note
26798        - Clipping is supported.
26799     **/
26800     template<typename tc>
26801     CImg<T>& draw_rectangle(const int x0, const int y0,
26802                             const int x1, const int y1,
26803                             const tc *const color, const float opacity=1) {
26804       return draw_rectangle(x0,y0,0,x1,y1,_depth-1,color,opacity);
26805     }
26806 
26807     //! Draw a 2d outlined colored rectangle.
26808     template<typename tc>
26809     CImg<T>& draw_rectangle(const int x0, const int y0,
26810                             const int x1, const int y1,
26811                             const tc *const color, const float opacity,
26812                             const unsigned int pattern) {
26813       if (is_empty()) return *this;
26814       if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
26815       if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
26816       const bool bx = (x0<x1), by = (y0<y1);
26817       const int
26818         nx0 = bx?x0:x1, nx1 = bx?x1:x0,
26819         ny0 = by?y0:y1, ny1 = by?y1:y0;
26820       if (ny1==ny0+1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
26821                       draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
26822       return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
26823         draw_line(nx1,ny0+1,nx1,ny1-1,color,opacity,pattern,false).
26824         draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
26825         draw_line(nx0,ny1-1,nx0,ny0+1,color,opacity,pattern,false);
26826     }
26827 
26828     //! Draw a filled polygon in the instance image.
26829     template<typename t, typename tc>
26830     CImg<T>& draw_polygon(const CImg<t>& points,
26831                           const tc *const color, const float opacity=1) {
26832       if (!color)
26833         throw CImgArgumentException(_cimg_instance
26834                                     "draw_polygon() : Specified color is (null).",
26835                                     cimg_instance);
26836 
26837       if (is_empty() || !points || points._width<3) return *this;
26838 
26839       // Normalize 2d input coordinates.
26840       CImg<intT> npoints(points._width,2);
26841       int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
26842       unsigned int nb_points = 1;
26843       for (unsigned int p = 1; p<points._width; ++p) {
26844         const int nx = (int)points(p,0), ny = (int)points(p,1);
26845         if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
26846       }
26847 
26848       if (nb_points==3) return draw_triangle((int)npoints(0,0),(int)npoints(0,1),
26849                                              (int)npoints(1,0),(int)npoints(1,1),
26850                                              (int)npoints(2,0),(int)npoints(2,1),color,opacity);
26851       // Draw polygon segments.
26852       _draw_scanline(color,opacity);
26853       int
26854         xmax = 0, xmin = (int)npoints.get_shared_points(0,nb_points-1,0).min_max(xmax),
26855         ymax = 0, ymin = (int)npoints.get_shared_points(0,nb_points-1,1).min_max(ymax);
26856       if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
26857       if (ymin==ymax) return _draw_scanline(xmin,xmax,ymin,color,opacity);
26858       const unsigned int
26859         nymin = ymin<0?0:(unsigned int)ymin,
26860         nymax = ymax>=height()?_height-1:(unsigned int)ymax,
26861         dy = 1 + nymax - nymin;
26862       CImg<intT> X(1+2*nb_points,dy,1,1,0), tmp;
26863       int cx = (int)npoints(0,0), cy = (int)npoints(0,1);
26864       unsigned int cp = 0;
26865       for (unsigned int p = 0; p<nb_points; ++p) {
26866         const unsigned int np = (p!=nb_points-1)?p+1:0, ap = (np!=nb_points-1)?np+1:0;
26867         const int
26868           nx = (int)npoints(np,0), ny = (int)npoints(np,1), ay = (int)npoints(ap,1),
26869           y0 = cy - nymin, y1 = ny - nymin;
26870         if (y0!=y1) {
26871           const int countermin = ((ny<ay && cy<ny) || (ny>ay && cy>ny))?1:0;
26872           for (int x = cx, y = y0, _sx = 1, _sy = 1,
26873                  _dx = nx>cx?nx-cx:((_sx=-1),cx-nx),
26874                  _dy = y1>y0?y1-y0:((_sy=-1),y0-y1),
26875                  _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy),
26876                  _err = _dx>>1,
26877                  _rx = _dy?(nx-cx)/_dy:0;
26878                _counter>=countermin;
26879                --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0))
26880             if (y>=0 && y<(int)dy) X(++X(0,y),y) = x;
26881           cp = np; cx = nx; cy = ny;
26882         } else {
26883           const int pp = (cp?cp-1:nb_points-1), py = (int)npoints(pp,1);
26884           if (y0>=0 && y0<(int)dy && (!p || (cy>py && ay>cy) || (cy<py && ay<cy))) X(++X(0,y0),y0) = nx;
26885           if (cy!=ay) { cp = np; cx = nx; cy = ny; }
26886         }
26887       }
26888 
26889       // Draw polygon scanlines.
26890       for (int y = 0; y<(int)dy; ++y) {
26891         tmp.assign(X.data(1,y),X(0,y),1,1,1,true).sort();
26892         for (int i = 1; i<=X(0,y); ) {
26893           const int xb = X(i++,y), xe = X(i++,y);
26894           _draw_scanline(xb,xe,nymin+y,color,opacity);
26895         }
26896       }
26897 
26898       return *this;
26899     }
26900 
26901     //! Draw a outlined polygon in the instance image.
26902     template<typename t, typename tc>
26903     CImg<T>& draw_polygon(const CImg<t>& points,
26904                           const tc *const color, const float opacity, const unsigned int pattern) {
26905       if (is_empty() || !points || points._width<3) return *this;
26906       bool ninit_hatch = true;
26907       switch (points._height) {
26908       case 0 : case 1 :
26909         throw CImgArgumentException(_cimg_instance
26910                                     "draw_polygon() : Invalid specified point set.",
26911                                     cimg_instance);
26912       case 2 : { // 2d version.
26913         CImg<intT> npoints(points._width,2);
26914         int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
26915         unsigned int nb_points = 1;
26916         for (unsigned int p = 1; p<points._width; ++p) {
26917           const int nx = (int)points(p,0), ny = (int)points(p,1);
26918           if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
26919         }
26920         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
26921         int ox = x0, oy = y0;
26922         for (unsigned int i = 1; i<nb_points; ++i) {
26923           const int x = (int)npoints(i,0), y = (int)npoints(i,1);
26924           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
26925           ninit_hatch = false;
26926           ox = x; oy = y;
26927         }
26928         draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
26929       } break;
26930       default : { // 3d version.
26931         CImg<intT> npoints(points._width,3);
26932         int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1), z = npoints(0,2) = (int)points(0,2);
26933         unsigned int nb_points = 1;
26934         for (unsigned int p = 1; p<points._width; ++p) {
26935           const int nx = (int)points(p,0), ny = (int)points(p,1), nz = (int)points(p,2);
26936           if (nx!=x || ny!=y || nz!=z) { npoints(nb_points,0) = nx; npoints(nb_points,1) = ny; npoints(nb_points++,2) = nz; x = nx; y = ny; z = nz; }
26937         }
26938         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1), z0 = (int)npoints(0,2);
26939         int ox = x0, oy = y0, oz = z0;
26940         for (unsigned int i = 1; i<nb_points; ++i) {
26941           const int x = (int)npoints(i,0), y = (int)npoints(i,1), z = (int)npoints(i,2);
26942           draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
26943           ninit_hatch = false;
26944           ox = x; oy = y; oz = z;
26945         }
26946         draw_line(ox,oy,oz,x0,y0,z0,color,opacity,pattern,false);
26947       }
26948       }
26949       return *this;
26950     }
26951 
26952     //! Draw a filled circle.
26953     /**
26954        \param x0 X-coordinate of the circle center.
26955        \param y0 Y-coordinate of the circle center.
26956        \param radius  Circle radius.
26957        \param color Array of spectrum() values of type \c T, defining the drawing color.
26958        \param opacity Drawing opacity.
26959        \note
26960        - Circle version of the Bresenham's algorithm is used.
26961     **/
26962     template<typename tc>
26963     CImg<T>& draw_circle(const int x0, const int y0, int radius,
26964                          const tc *const color, const float opacity=1) {
26965       if (!color)
26966         throw CImgArgumentException(_cimg_instance
26967                                     "draw_circle : Specified color is (null).",
26968                                     cimg_instance);
26969 
26970       if (is_empty()) return *this;
26971       _draw_scanline(color,opacity);
26972       if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
26973       if (y0>=0 && y0<height()) _draw_scanline(x0-radius,x0+radius,y0,color,opacity);
26974       for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
26975         if (f>=0) {
26976           const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y;
26977           if (y1>=0 && y1<height()) _draw_scanline(x1,x2,y1,color,opacity);
26978           if (y2>=0 && y2<height()) _draw_scanline(x1,x2,y2,color,opacity);
26979           f+=(ddFy+=2); --y;
26980         }
26981         const bool no_diag = y!=(x++);
26982         ++(f+=(ddFx+=2));
26983         const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x;
26984         if (no_diag) {
26985           if (y1>=0 && y1<height()) _draw_scanline(x1,x2,y1,color,opacity);
26986           if (y2>=0 && y2<height()) _draw_scanline(x1,x2,y2,color,opacity);
26987         }
26988       }
26989       return *this;
26990     }
26991 
26992     //! Draw an outlined circle.
26993     /**
26994        \param x0 X-coordinate of the circle center.
26995        \param y0 Y-coordinate of the circle center.
26996        \param radius Circle radius.
26997        \param color Array of spectrum() values of type \c T, defining the drawing color.
26998        \param opacity Drawing opacity.
26999     **/
27000     template<typename tc>
27001     CImg<T>& draw_circle(const int x0, const int y0, int radius,
27002                          const tc *const color, const float opacity,
27003                          const unsigned int) {
27004       if (!color)
27005         throw CImgArgumentException(_cimg_instance
27006                                     "draw_circle : Specified color is (null).",
27007                                     cimg_instance);
27008 
27009       if (is_empty()) return *this;
27010       if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
27011       if (!radius) return draw_point(x0,y0,color,opacity);
27012       draw_point(x0-radius,y0,color,opacity).draw_point(x0+radius,y0,color,opacity).
27013         draw_point(x0,y0-radius,color,opacity).draw_point(x0,y0+radius,color,opacity);
27014       if (radius==1) return *this;
27015       for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
27016         if (f>=0) { f+=(ddFy+=2); --y; }
27017         ++x; ++(f+=(ddFx+=2));
27018         if (x!=y+1) {
27019           const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x, x3 = x0-x, x4 = x0+x, y3 = y0-y, y4 = y0+y;
27020           draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
27021             draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
27022           if (x!=y)
27023             draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
27024               draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
27025         }
27026       }
27027       return *this;
27028     }
27029 
27030     // Draw a 2d ellipse (inner routine).
27031     template<typename tc>
27032     CImg<T>& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
27033                            const tc *const color, const float opacity,
27034                            const unsigned int pattern) {
27035       if (!color)
27036         throw CImgArgumentException(_cimg_instance
27037                                     "draw_ellipse : Specified color is (null).",
27038                                     cimg_instance);
27039 
27040       if (is_empty()) return *this;
27041       _draw_scanline(color,opacity);
27042       const float
27043         nr1 = cimg::abs(r1), nr2 = cimg::abs(r2),
27044         nangle = (float)(angle*cimg::PI/180),
27045         u = (float)std::cos(nangle),
27046         v = (float)std::sin(nangle),
27047         rmax = cimg::max(nr1,nr2),
27048         l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2),
27049         l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2),
27050         a = l1*u*u + l2*v*v,
27051         b = u*v*(l1-l2),
27052         c = l1*v*v + l2*u*u;
27053       const int
27054         yb = (int)std::sqrt(a*rmax*rmax/(a*c-b*b)),
27055         tymin = y0 - yb - 1,
27056         tymax = y0 + yb + 1,
27057         ymin = tymin<0?0:tymin,
27058         ymax = tymax>=height()?_height-1:tymax;
27059       int oxmin = 0, oxmax = 0;
27060       bool first_line = true;
27061       for (int y = ymin; y<=ymax; ++y) {
27062         const float
27063           Y = y-y0 + (y<y0?0.5f:-0.5f),
27064           delta = b*b*Y*Y-a*(c*Y*Y-rmax*rmax),
27065           sdelta = delta>0?(float)std::sqrt(delta)/a:0.0f,
27066           bY = b*Y/a,
27067           fxmin = x0-0.5f-bY-sdelta,
27068           fxmax = x0+0.5f-bY+sdelta;
27069         const int xmin = (int)fxmin, xmax = (int)fxmax;
27070         if (!pattern) _draw_scanline(xmin,xmax,y,color,opacity);
27071         else {
27072           if (first_line) {
27073             if (y0-yb>=0) _draw_scanline(xmin,xmax,y,color,opacity);
27074             else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity);
27075             first_line = false;
27076           } else {
27077             if (xmin<oxmin) _draw_scanline(xmin,oxmin-1,y,color,opacity);
27078             else _draw_scanline(oxmin+(oxmin==xmin?0:1),xmin,y,color,opacity);
27079             if (xmax<oxmax) _draw_scanline(xmax,oxmax-1,y,color,opacity);
27080             else _draw_scanline(oxmax+(oxmax==xmax?0:1),xmax,y,color,opacity);
27081             if (y==tymax) _draw_scanline(xmin+1,xmax-1,y,color,opacity);
27082           }
27083         }
27084         oxmin = xmin; oxmax = xmax;
27085       }
27086       return *this;
27087     }
27088 
27089     //! Draw a filled ellipse.
27090     /**
27091        \param x0 = X-coordinate of the ellipse center.
27092        \param y0 = Y-coordinate of the ellipse center.
27093        \param r1 = First radius of the ellipse.
27094        \param r2 = Second radius of the ellipse.
27095        \param angle = Angle of the first radius.
27096        \param color = array of spectrum() values of type \c T, defining the drawing color.
27097        \param opacity = opacity of the drawing.
27098     **/
27099     template<typename tc>
27100     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
27101                           const tc *const color, const float opacity=1) {
27102       return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U);
27103     }
27104 
27105     //! Draw a filled ellipse.
27106     /**
27107        \param x0 = X-coordinate of the ellipse center.
27108        \param y0 = Y-coordinate of the ellipse center.
27109        \param tensor = Diffusion tensor describing the ellipse.
27110        \param color = array of spectrum() values of type \c T, defining the drawing color.
27111        \param opacity = opacity of the drawing.
27112     **/
27113     template<typename t, typename tc>
27114     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
27115                           const tc *const color, const float opacity=1) {
27116       CImgList<t> eig = tensor.get_symmetric_eigen();
27117       const CImg<t> &val = eig[0], &vec = eig[1];
27118       return draw_ellipse(x0,y0,val(0),val(1),vec(0,0),vec(0,1),color,opacity);
27119     }
27120 
27121     //! Draw an outlined ellipse.
27122     /**
27123        \param x0 = X-coordinate of the ellipse center.
27124        \param y0 = Y-coordinate of the ellipse center.
27125        \param r1 = First radius of the ellipse.
27126        \param r2 = Second radius of the ellipse.
27127        \param ru = X-coordinate of the orientation vector related to the first radius.
27128        \param rv = Y-coordinate of the orientation vector related to the first radius.
27129        \param color = array of spectrum() values of type \c T, defining the drawing color.
27130        \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern.
27131        \param opacity = opacity of the drawing.
27132     **/
27133     template<typename tc>
27134     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
27135                           const tc *const color, const float opacity, const unsigned int pattern) {
27136       if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern);
27137       return *this;
27138     }
27139 
27140     //! Draw an outlined ellipse.
27141     /**
27142        \param x0 = X-coordinate of the ellipse center.
27143        \param y0 = Y-coordinate of the ellipse center.
27144        \param tensor = Diffusion tensor describing the ellipse.
27145        \param color = array of spectrum() values of type \c T, defining the drawing color.
27146        \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern.
27147        \param opacity = opacity of the drawing.
27148     **/
27149     template<typename t, typename tc>
27150     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
27151                           const tc *const color, const float opacity,
27152                           const unsigned int pattern) {
27153       CImgList<t> eig = tensor.get_symmetric_eigen();
27154       const CImg<t> &val = eig[0], &vec = eig[1];
27155       return draw_ellipse(x0,y0,val(0),val(1),vec(0,0),vec(0,1),color,opacity,pattern);
27156     }
27157 
27158     //! Draw an image.
27159     /**
27160        \param sprite Sprite image.
27161        \param x0 X-coordinate of the sprite position.
27162        \param y0 Y-coordinate of the sprite position.
27163        \param z0 Z-coordinate of the sprite position.
27164        \param c0 C-coordinate of the sprite position.
27165        \param opacity Drawing opacity (optional).
27166        \note
27167        - Clipping is supported.
27168     **/
27169     template<typename t>
27170     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
27171                         const CImg<t>& sprite, const float opacity=1) {
27172       if (!sprite)
27173         throw CImgArgumentException(_cimg_instance
27174                                     "draw_image() : Empty specified sprite.",
27175                                     cimg_instance);
27176 
27177       if (is_empty()) return *this;
27178       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
27179       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
27180       const int
27181         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
27182         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
27183         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
27184         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
27185       const t
27186         *ptrs = sprite._data -
27187         (bx?x0:0) -
27188         (by?y0*sprite.width():0) -
27189         (bz?z0*sprite.width()*sprite.height():0) -
27190         (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
27191       const unsigned int
27192         offX = _width - lX, soffX = sprite._width - lX,
27193         offY = _width*(_height - lY), soffY = sprite._width*(sprite._height - lY),
27194         offZ = _width*_height*(_depth - lZ), soffZ = sprite._width*sprite._height*(sprite._depth - lZ);
27195       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27196       if (lX>0 && lY>0 && lZ>0 && lC>0) {
27197         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
27198         for (int v = 0; v<lC; ++v) {
27199           for (int z = 0; z<lZ; ++z) {
27200             for (int y = 0; y<lY; ++y) {
27201               if (opacity>=1) for (int x = 0; x<lX; ++x) *(ptrd++) = (T)*(ptrs++);
27202               else for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
27203               ptrd+=offX; ptrs+=soffX;
27204             }
27205             ptrd+=offY; ptrs+=soffY;
27206           }
27207           ptrd+=offZ; ptrs+=soffZ;
27208         }
27209       }
27210       return *this;
27211     }
27212 
27213     // Optimized version (internal).
27214     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
27215                         const CImg<T>& sprite, const float opacity=1) {
27216       if (!sprite)
27217         throw CImgArgumentException(_cimg_instance
27218                                     "draw_image() : Empty specified sprite.",
27219                                     cimg_instance);
27220 
27221       if (is_empty()) return *this;
27222       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
27223       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
27224       const int
27225         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
27226         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
27227         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
27228         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
27229       const T
27230         *ptrs = sprite._data -
27231         (bx?x0:0) -
27232         (by?y0*sprite.width():0) -
27233         (bz?z0*sprite.width()*sprite.height():0) -
27234         (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
27235       const unsigned int
27236         offX = _width - lX, soffX = sprite._width - lX,
27237         offY = _width*(_height - lY), soffY = sprite._width*(sprite._height - lY),
27238         offZ = _width*_height*(_depth - lZ), soffZ = sprite._width*sprite._height*(sprite._depth - lZ),
27239         slX = lX*sizeof(T);
27240       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27241       if (lX>0 && lY>0 && lZ>0 && lC>0) {
27242         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
27243         for (int v = 0; v<lC; ++v) {
27244           for (int z = 0; z<lZ; ++z) {
27245             if (opacity>=1) for (int y = 0; y<lY; ++y) { std::memcpy(ptrd,ptrs,slX); ptrd+=_width; ptrs+=sprite._width; }
27246             else for (int y = 0; y<lY; ++y) {
27247               for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
27248               ptrd+=offX; ptrs+=soffX;
27249             }
27250             ptrd+=offY; ptrs+=soffY;
27251           }
27252           ptrd+=offZ; ptrs+=soffZ;
27253         }
27254       }
27255       return *this;
27256     }
27257 
27258     //! Draw an image.
27259     template<typename t>
27260     CImg<T>& draw_image(const int x0, const int y0, const int z0,
27261                         const CImg<t>& sprite, const float opacity=1) {
27262       return draw_image(x0,y0,z0,0,sprite,opacity);
27263     }
27264 
27265     //! Draw an image.
27266     template<typename t>
27267     CImg<T>& draw_image(const int x0, const int y0,
27268                         const CImg<t>& sprite, const float opacity=1) {
27269       return draw_image(x0,y0,0,sprite,opacity);
27270     }
27271 
27272     //! Draw an image.
27273     template<typename t>
27274     CImg<T>& draw_image(const int x0,
27275                         const CImg<t>& sprite, const float opacity=1) {
27276       return draw_image(x0,0,sprite,opacity);
27277     }
27278 
27279     //! Draw an image.
27280     template<typename t>
27281     CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
27282       return draw_image(0,sprite,opacity);
27283     }
27284 
27285     //! Draw a sprite image in the instance image (masked version).
27286     /**
27287        \param sprite Sprite image.
27288        \param mask Mask image.
27289        \param x0 X-coordinate of the sprite position in the instance image.
27290        \param y0 Y-coordinate of the sprite position in the instance image.
27291        \param z0 Z-coordinate of the sprite position in the instance image.
27292        \param c0 C-coordinate of the sprite position in the instance image.
27293        \param mask_valmax Maximum pixel value of the mask image \c mask (optional).
27294        \param opacity Drawing opacity.
27295        \note
27296        - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
27297        - Clipping is supported.
27298        - Dimensions along x,y and z of \p sprite and \p mask must be the same.
27299     **/
27300     template<typename ti, typename tm>
27301     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
27302                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
27303                         const float mask_valmax=1) {
27304       if (!sprite)
27305         throw CImgArgumentException(_cimg_instance
27306                                     "draw_image() : Empty specified sprite.",
27307                                     cimg_instance);
27308       if (!mask)
27309         throw CImgArgumentException(_cimg_instance
27310                                     "draw_image() : Empty specified mask.",
27311                                     cimg_instance);
27312 
27313       if (is_empty()) return *this;
27314       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_valmax);
27315       if (is_overlapped(mask))   return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_valmax);
27316       if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
27317         throw CImgArgumentException(_cimg_instance
27318                                     "draw_image() : Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have incompatible dimensions.",
27319                                     cimg_instance,
27320                                     sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
27321                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
27322 
27323       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
27324       const int
27325         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
27326         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
27327         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
27328         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
27329       const int
27330         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),
27331         ssize = mask.width()*mask.height()*mask.depth();
27332       const ti *ptrs = sprite._data + coff;
27333       const tm *ptrm = mask._data   + coff;
27334       const unsigned int
27335         offX = _width - lX, soffX = sprite._width - lX,
27336         offY = _width*(_height - lY), soffY = sprite._width*(sprite._height - lY),
27337         offZ = _width*_height*(_depth - lZ), soffZ = sprite._width*sprite._height*(sprite._depth - lZ);
27338       if (lX>0 && lY>0 && lZ>0 && lC>0) {
27339         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
27340         for (int c = 0; c<lC; ++c) {
27341           ptrm = mask._data + (ptrm - mask._data)%ssize;
27342           for (int z = 0; z<lZ; ++z) {
27343             for (int y = 0; y<lY; ++y) {
27344               for (int x = 0; x<lX; ++x) {
27345                 const float mopacity = (float)(*(ptrm++)*opacity),
27346                   nopacity = cimg::abs(mopacity), copacity = mask_valmax - cimg::max(mopacity,0);
27347                 *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_valmax);
27348                 ++ptrd;
27349               }
27350               ptrd+=offX; ptrs+=soffX; ptrm+=soffX;
27351             }
27352             ptrd+=offY; ptrs+=soffY; ptrm+=soffY;
27353           }
27354           ptrd+=offZ; ptrs+=soffZ; ptrm+=soffZ;
27355         }
27356       }
27357       return *this;
27358     }
27359 
27360     //! Draw an image.
27361     template<typename ti, typename tm>
27362     CImg<T>& draw_image(const int x0, const int y0, const int z0,
27363                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
27364                         const float mask_valmax=1) {
27365       return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_valmax);
27366     }
27367 
27368     //! Draw an image.
27369     template<typename ti, typename tm>
27370     CImg<T>& draw_image(const int x0, const int y0,
27371                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
27372                         const float mask_valmax=1) {
27373       return draw_image(x0,y0,0,sprite,mask,opacity,mask_valmax);
27374     }
27375 
27376     //! Draw an image.
27377     template<typename ti, typename tm>
27378     CImg<T>& draw_image(const int x0,
27379                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
27380                         const float mask_valmax=1) {
27381       return draw_image(x0,0,sprite,mask,opacity,mask_valmax);
27382     }
27383 
27384     //! Draw an image.
27385     template<typename ti, typename tm>
27386     CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
27387                         const float mask_valmax=1) {
27388       return draw_image(0,sprite,mask,opacity,mask_valmax);
27389     }
27390 
27391     //! Draw a text.
27392     /**
27393        \param x0 X-coordinate of the text in the instance image.
27394        \param y0 Y-coordinate of the text in the instance image.
27395        \param foreground_color Array of spectrum() values of type \c T, defining the foreground color (0 means 'transparent').
27396        \param background_color Array of spectrum() values of type \c T, defining the background color (0 means 'transparent').
27397        \param font Font used for drawing text.
27398        \param opacity Drawing opacity.
27399        \param format 'printf'-style format string, followed by arguments.
27400        \note Clipping is supported.
27401     **/
27402     template<typename tc1, typename tc2, typename t>
27403     CImg<T>& draw_text(const int x0, const int y0,
27404                        const char *const text,
27405                        const tc1 *const foreground_color, const tc2 *const background_color,
27406                        const float opacity, const CImgList<t>& font, ...) {
27407       if (!font) return *this;
27408       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
27409       std::vsprintf(tmp,text,ap); va_end(ap);
27410       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font);
27411     }
27412 
27413     template<typename tc, typename t>
27414     CImg<T>& draw_text(const int x0, const int y0,
27415                        const char *const text,
27416                        const tc *const foreground_color, const int,
27417                        const float opacity, const CImgList<t>& font, ...) {
27418       if (!font) return *this;
27419       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
27420       std::vsprintf(tmp,text,ap); va_end(ap);
27421       return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font);
27422     }
27423 
27424     template<typename tc, typename t>
27425     CImg<T>& draw_text(const int x0, const int y0,
27426                        const char *const text,
27427                        const int, const tc *const background_color,
27428                        const float opacity, const CImgList<t>& font, ...) {
27429       if (!font) return *this;
27430       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
27431       std::vsprintf(tmp,text,ap); va_end(ap);
27432       return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font);
27433     }
27434 
27435     //! Draw a text.
27436     /**
27437        \param x0 X-coordinate of the text in the instance image.
27438        \param y0 Y-coordinate of the text in the instance image.
27439        \param foreground_color Array of spectrum() values of type \c T, defining the foreground color (0 means 'transparent').
27440        \param background_color Array of spectrum() values of type \c T, defining the background color (0 means 'transparent').
27441        \param font_size Size of the font (exact match for 13,24,32,57).
27442        \param opacity Drawing opacity.
27443        \param format 'printf'-style format string, followed by arguments.
27444        \note Clipping is supported.
27445     **/
27446     template<typename tc1, typename tc2>
27447     CImg<T>& draw_text(const int x0, const int y0,
27448                        const char *const text,
27449                        const tc1 *const foreground_color, const tc2 *const background_color,
27450                        const float opacity=1, const unsigned int font_height=13, ...) {
27451       if (!font_height) return *this;
27452       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); std::vsprintf(tmp,text,ap); va_end(ap);
27453       static CImgList<floatT> font;
27454       const unsigned int
27455         ref_height = font_height<=13?13:font_height<=28?24:font_height<=32?32:57,
27456         padding_x = font_height<=18?1:font_height<=32?2:3;
27457       if (!font || font[0]._height!=font_height) {
27458         font = CImgList<floatT>::font(ref_height,true);
27459         font[0].assign(1,font_height);
27460         if (ref_height==font_height) cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,-100,-100,0);
27461       }
27462       if (ref_height!=font_height) for (const char *ptrs = tmp; *ptrs; ++ptrs) {
27463           const unsigned int _c = (unsigned int)(unsigned char)*ptrs;
27464           if (_c<font._width) {
27465             CImg<floatT> &c = font[_c];
27466             if (c._height!=font_height) {
27467               c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:5);
27468               c.resize(c._width + padding_x,-100,-100,-100,0);
27469             }
27470           }
27471           if (_c+256U<font._width) {
27472             CImg<floatT> &c = font[_c+256];
27473             if (c._height!=font_height) {
27474               c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:5);
27475               c.resize(c._width + padding_x,-100,-100,-100,0);
27476             }
27477           }
27478         }
27479       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font);
27480     }
27481 
27482     template<typename tc>
27483     CImg<T>& draw_text(const int x0, const int y0,
27484                        const char *const text,
27485                        const tc *const foreground_color, const int background_color=0,
27486                        const float opacity=1, const unsigned int font_height=13, ...) {
27487       if (!font_height) return *this;
27488       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); std::vsprintf(tmp,text,ap); va_end(ap);
27489       return draw_text(x0,y0,tmp,foreground_color,(const tc*)background_color,opacity,font_height);
27490     }
27491 
27492     template<typename tc>
27493     CImg<T>& draw_text(const int x0, const int y0,
27494                        const char *const text,
27495                        const int, const tc *const background_color,
27496                        const float opacity=1, const unsigned int font_height=13, ...) {
27497       if (!font_height) return *this;
27498       char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); std::vsprintf(tmp,text,ap); va_end(ap);
27499       return draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font_height);
27500     }
27501 
27502     // Draw a text (internal routine).
27503     template<typename tc1, typename tc2, typename t>
27504     CImg<T>& _draw_text(const int x0, const int y0,
27505                         const char *const text,
27506                         const tc1 *const foreground_color, const tc2 *const background_color,
27507                         const float opacity, const CImgList<t>& font) {
27508       if (!text) return *this;
27509       if (!font)
27510         throw CImgArgumentException(_cimg_instance
27511                                     "draw_text() : Empty specified font.",
27512                                     cimg_instance);
27513 
27514       const unsigned int text_length = std::strlen(text);
27515       if (is_empty()) {
27516         // If needed, pre-compute necessary size of the image
27517         int x = 0, y = 0, w = 0;
27518         unsigned char c = 0;
27519         for (unsigned int i = 0; i<text_length; ++i) {
27520           c = text[i];
27521           switch (c) {
27522           case '\n' : y+=font[' ']._height; if (x>w) w = x; x = 0; break;
27523           case '\t' : x+=4*font[' ']._width; break;
27524           default : if (c<font._width) x+=font[c]._width;
27525           }
27526         }
27527         if (x!=0 || c=='\n') {
27528           if (x>w) w=x;
27529           y+=font[' ']._height;
27530         }
27531         assign(x0+w,y0+y,1,font[' ']._spectrum,0);
27532         if (background_color) cimg_forC(*this,c) get_shared_channel(c).fill((T)background_color[c]);
27533       }
27534 
27535       int x = x0, y = y0;
27536       CImg<t> letter;
27537       for (unsigned int i = 0; i<text_length; ++i) {
27538         const unsigned char c = text[i];
27539         switch (c) {
27540         case '\n' : y+=font[' ']._height; x = x0; break;
27541         case '\t' : x+=4*font[' ']._width; break;
27542         default : if (c<font._width) {
27543           letter = font[c];
27544           const CImg<t>& mask = (c+256)<(int)font._width?font[c+256]:font[c];
27545           if (foreground_color)
27546             for (unsigned int p = 0; p<letter._width*letter._height; ++p)
27547               if (mask(p)) cimg_forC(*this,c) letter(p,0,0,c) = (t)(letter(p,0,0,c)*foreground_color[c]);
27548           if (background_color)
27549             for (unsigned int p = 0; p<letter._width*letter._height; ++p)
27550               if (!mask(p)) cimg_forC(*this,c) letter(p,0,0,c) = (t)background_color[c];
27551           if (!background_color && font._width>=512) draw_image(x,y,letter,mask,opacity,(T)1);
27552           else draw_image(x,y,letter,opacity);
27553           x+=letter._width;
27554         }
27555         }
27556       }
27557       return *this;
27558     }
27559 
27560     //! Draw a vector field in the instance image, using a colormap.
27561     /**
27562        \param flow Image of 2d vectors used as input data.
27563        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
27564        \param sampling Length (in pixels) between each arrow.
27565        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
27566        \param opacity Opacity of the drawing.
27567        \param pattern Used pattern to draw lines.
27568        \note Clipping is supported.
27569     **/
27570     template<typename t1, typename t2>
27571     CImg<T>& draw_quiver(const CImg<t1>& flow,
27572                          const t2 *const color, const float opacity=1,
27573                          const unsigned int sampling=25, const float factor=-20,
27574                          const bool arrows=true, const unsigned int pattern=~0U) {
27575       return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,arrows,pattern);
27576     }
27577 
27578     //! Draw a vector field in the instance image, using a colormap.
27579     /**
27580        \param flow Image of 2d vectors used as input data.
27581        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
27582        \param sampling Length (in pixels) between each arrow.
27583        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
27584        \param opacity Opacity of the drawing.
27585        \param pattern Used pattern to draw lines.
27586        \note Clipping is supported.
27587     **/
27588     template<typename t1, typename t2>
27589     CImg<T>& draw_quiver(const CImg<t1>& flow,
27590                          const CImg<t2>& color, const float opacity=1,
27591                          const unsigned int sampling=25, const float factor=-20,
27592                          const bool arrows=true, const unsigned int pattern=~0U) {
27593 
27594       if (is_empty()) return *this;
27595 
27596       if (!flow || flow._spectrum!=2)
27597         throw CImgArgumentException(_cimg_instance
27598                                     "draw_quiver() : Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
27599                                     cimg_instance,
27600                                     flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
27601       if (sampling<=0)
27602         throw CImgArgumentException(_cimg_instance
27603                                     "draw_quiver() : Invalid sampling value %g "
27604                                     "(should be >0)",
27605                                     cimg_instance,
27606                                     sampling);
27607 
27608       const bool colorfield = (color._width==flow._width && color._height==flow._height && color._depth==1 && color._spectrum==_spectrum);
27609       if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,arrows,pattern);
27610 
27611       float vmax,fact;
27612       if (factor<=0) {
27613         float m, M = (float)flow.get_norm(2).max_min(m);
27614         vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M));
27615         fact = -factor;
27616       } else { fact = factor; vmax = 1; }
27617 
27618       for (unsigned int y = sampling/2; y<_height; y+=sampling)
27619         for (unsigned int x = sampling/2; x<_width; x+=sampling) {
27620           const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
27621           float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
27622           if (arrows) {
27623             const int xx = x+(int)u, yy = y+(int)v;
27624             if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.0f,pattern);
27625             else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.0f,pattern);
27626           } else {
27627             if (colorfield) draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),color.get_vector_at(X,Y)._data,opacity,pattern);
27628             else draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),color._data,opacity,pattern);
27629           }
27630         }
27631 
27632       return *this;
27633     }
27634 
27635     //! Draw a labeled horizontal axis on the instance image.
27636     /**
27637        \param xvalues Lower bound of the x-range.
27638        \param y Y-coordinate of the horizontal axis in the instance image.
27639        \param color Array of spectrum() values of type \c T, defining the drawing color.
27640        \param opacity Drawing opacity.
27641        \param pattern Drawing pattern.
27642        \param opacity_out Drawing opacity of 'outside' axes.
27643        \note if \c precision==0, precision of the labels is automatically computed.
27644     **/
27645     template<typename t, typename tc>
27646     CImg<T>& draw_axis(const CImg<t>& xvalues, const int y,
27647                        const tc *const color, const float opacity=1,
27648                        const unsigned int pattern=~0U) {
27649       if (!is_empty()) {
27650         int siz = (int)xvalues.size()-1;
27651         if (siz<=0) draw_line(0,y,_width-1,y,color,opacity,pattern);
27652         else {
27653           if (xvalues[0]<xvalues[siz]) draw_arrow(0,y,_width-1,y,color,opacity,30,5,pattern);
27654           else draw_arrow(_width-1,y,0,y,color,opacity,30,5,pattern);
27655           const int yt = (y+14)<height()?(y+3):(y-14);
27656           char txt[32] = { 0 };
27657           cimg_foroff(xvalues,x) {
27658             std::sprintf(txt,"%g",(double)xvalues(x));
27659             const int xi = (int)(x*(_width-1)/siz), xt = xi-(int)std::strlen(txt)*3;
27660             draw_point(xi,y-1,color,opacity).draw_point(xi,y+1,color,opacity).
27661               draw_text(xt<0?0:xt,yt,txt,color,(tc*)0,opacity,13);
27662           }
27663         }
27664       }
27665       return *this;
27666     }
27667 
27668     //! Draw a labeled vertical axis on the instance image.
27669     template<typename t, typename tc>
27670     CImg<T>& draw_axis(const int x, const CImg<t>& yvalues,
27671                        const tc *const color, const float opacity=1,
27672                        const unsigned int pattern=~0U) {
27673       if (!is_empty()) {
27674         int siz = (int)yvalues.size()-1;
27675         if (siz<=0) draw_line(x,0,x,_height-1,color,opacity,pattern);
27676         else {
27677           if (yvalues[0]<yvalues[siz]) draw_arrow(x,0,x,_height-1,color,opacity,30,5,pattern);
27678           else draw_arrow(x,_height-1,x,0,color,opacity,30,5,pattern);
27679           char txt[32] = { 0 };
27680           cimg_foroff(yvalues,y) {
27681             std::sprintf(txt,"%g",(double)yvalues(y));
27682             const int
27683               yi = (int)(y*(_height-1)/siz),
27684               tmp = yi-5,
27685               nyi = tmp<0?0:(tmp>=height()-11?height()-11:tmp),
27686               xt = x-(int)std::strlen(txt)*7;
27687             draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity);
27688             if (xt>0) draw_text(xt,nyi,txt,color,(tc*)0,opacity,13);
27689             else draw_text(x+3,nyi,txt,color,(tc*)0,opacity,13);
27690           }
27691         }
27692       }
27693       return *this;
27694     }
27695 
27696     //! Draw a labeled horizontal+vertical axis on the instance image.
27697     template<typename tx, typename ty, typename tc>
27698       CImg<T>& draw_axes(const CImg<tx>& xvalues, const CImg<ty>& yvalues,
27699                          const tc *const color, const float opacity=1,
27700                          const unsigned int patternx=~0U, const unsigned int patterny=~0U) {
27701       if (!is_empty()) {
27702         const CImg<tx> nxvalues(xvalues._data,xvalues.size(),1,1,1,true);
27703         const int sizx = (int)xvalues.size()-1, wm1 = width()-1;
27704         if (sizx>0) {
27705           float ox = (float)nxvalues[0];
27706           for (unsigned int x = 1; x<_width; ++x) {
27707             const float nx = (float)nxvalues._linear_atX((float)x*sizx/wm1);
27708             if (nx*ox<=0) { draw_axis(nx==0?x:x-1,yvalues,color,opacity,patterny); break; }
27709             ox = nx;
27710           }
27711         }
27712         const CImg<ty> nyvalues(yvalues._data,yvalues.size(),1,1,1,true);
27713         const int sizy = (int)yvalues.size()-1, hm1 = height()-1;
27714         if (sizy>0) {
27715           float oy = (float)nyvalues[0];
27716           for (unsigned int y = 1; y<_height; ++y) {
27717             const float ny = (float)nyvalues._linear_atX((float)y*sizy/hm1);
27718             if (ny*oy<=0) { draw_axis(xvalues,ny==0?y:y-1,color,opacity,patternx); break; }
27719             oy = ny;
27720           }
27721         }
27722       }
27723       return *this;
27724     }
27725 
27726     //! Draw a labeled horizontal+vertical axis on the instance image.
27727     template<typename tc>
27728     CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
27729                        const tc *const color, const float opacity=1,
27730                        const int subdivisionx=-60, const int subdivisiony=-60,
27731                        const float precisionx=0, const float precisiony=0,
27732                        const unsigned int patternx=~0U, const unsigned int patterny=~0U) {
27733       if (!is_empty()) {
27734         const float
27735           dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0),
27736           px = (precisionx==0)?(float)std::pow(10.0,(int)std::log10(dx)-2.0):precisionx,
27737           py = (precisiony==0)?(float)std::pow(10.0,(int)std::log10(dy)-2.0):precisiony;
27738         draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),
27739                   CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
27740                   color,opacity,patternx,patterny);
27741       }
27742       return *this;
27743     }
27744 
27745     //! Draw grid.
27746     template<typename tx, typename ty, typename tc>
27747     CImg<T>& draw_grid(const CImg<tx>& xvalues, const CImg<ty>& yvalues,
27748                        const tc *const color, const float opacity=1,
27749                        const unsigned int patternx=~0U, const unsigned int patterny=~0U) {
27750       if (!is_empty()) {
27751         if (xvalues) cimg_foroff(xvalues,x) {
27752           const int xi = (int)xvalues[x];
27753           if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height-1,color,opacity,patternx);
27754         }
27755         if (yvalues) cimg_foroff(yvalues,y) {
27756           const int yi = (int)yvalues[y];
27757           if (yi>=0 && yi<height()) draw_line(0,yi,_width-1,yi,color,opacity,patterny);
27758         }
27759       }
27760       return *this;
27761     }
27762 
27763     //! Draw grid.
27764     template<typename tc>
27765     CImg<T>& draw_grid(const float deltax,  const float deltay,
27766                        const float offsetx, const float offsety,
27767                        const bool invertx, const bool inverty,
27768                        const tc *const color, const float opacity=1,
27769                        const unsigned int patternx=~0U, const unsigned int patterny=~0U) {
27770       CImg<uintT> seqx, seqy;
27771       if (deltax!=0) {
27772         const float dx = deltax>0?deltax:_width*-deltax/100;
27773         const unsigned int nx = (unsigned int)(_width/dx);
27774         seqx = CImg<uintT>::sequence(1+nx,0,(unsigned int)(dx*nx));
27775         if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)_width);
27776         if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
27777       }
27778 
27779       if (deltay!=0) {
27780         const float dy = deltay>0?deltay:_height*-deltay/100;
27781         const unsigned int ny = (unsigned int)(_height/dy);
27782         seqy = CImg<uintT>::sequence(1+ny,0,(unsigned int)(dy*ny));
27783         if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)_height);
27784         if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
27785      }
27786       return draw_grid(seqx,seqy,color,opacity,patternx,patterny);
27787     }
27788 
27789     //! Draw a 1d graph on the instance image.
27790     /**
27791        \param data Image containing the graph values I = f(x).
27792        \param color Array of spectrum() values of type \c T, defining the drawing color.
27793        \param opacity Drawing opacity.
27794 
27795        \param plot_type Define the type of the plot :
27796                       - 0 = No plot.
27797                       - 1 = Plot using segments.
27798                       - 2 = Plot using cubic splines.
27799                       - 3 = Plot with bars.
27800        \param vertex_type Define the type of points :
27801                       - 0 = No points.
27802                       - 1 = Point.
27803                       - 2 = Straight cross.
27804                       - 3 = Diagonal cross.
27805                       - 4 = Filled circle.
27806                       - 5 = Outlined circle.
27807                       - 6 = Square.
27808                       - 7 = Diamond.
27809        \param ymin Lower bound of the y-range.
27810        \param ymax Upper bound of the y-range.
27811        \param expand Expand plot along the X-axis.
27812        \param pattern Drawing pattern.
27813        \note
27814          - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
27815     **/
27816     template<typename t, typename tc>
27817     CImg<T>& draw_graph(const CImg<t>& data,
27818                         const tc *const color, const float opacity=1,
27819                         const unsigned int plot_type=1, const int vertex_type=1,
27820                         const double ymin=0, const double ymax=0, const bool expand=false,
27821                         const unsigned int pattern=~0U) {
27822       if (!color)
27823         throw CImgArgumentException(_cimg_instance
27824                                     "draw_graph() : Specified color is (null).",
27825                                     cimg_instance);
27826 
27827       if (is_empty() || _height<=1) return *this;
27828       const unsigned int siz = data.size();
27829       tc *color1 = 0, *color2 = 0;
27830       if (plot_type==3) {
27831         color1 = new tc[_spectrum]; color2 = new tc[_spectrum];
27832         cimg_forC(*this,c) { color1[c] = (tc)(color[c]*0.6f); color2[c] = (tc)(color[c]*0.3f); }
27833       }
27834 
27835       double m = ymin, M = ymax;
27836       if (ymin==ymax) m = (double)data.max_min(M);
27837       if (m==M) { --m; ++M; }
27838       const float ca = (float)(M-m)/(_height-1);
27839       bool init_hatch = true;
27840       const unsigned int xp = expand?1:0;
27841 
27842       // Draw graph edges
27843       switch (plot_type%4) {
27844       case 1 : { // Segments
27845         int oX = 0, oY = (int)((data[0]-m)/ca);
27846         for (unsigned int off = 1; off<siz; ++off) {
27847           const int
27848             X = (int)(off*_width/(siz-xp)),
27849             Y = (int)((data[off]-m)/ca);
27850           draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
27851           oX = X; oY = Y;
27852           init_hatch = false;
27853         }
27854       } break;
27855       case 2 : { // Spline
27856         const CImg<t> ndata(data._data,siz,1,1,1,true);
27857         int oY = (int)((data[0]-m)/ca);
27858         const int xmax = (int)(_width*(ndata._width-1-xp)/ndata._width);
27859         for (int x = 0; x<xmax; ++x) {
27860           const int Y = (int)((ndata._cubic_atX((float)x*(ndata._width-xp)/_width)-m)/ca);
27861           if (x>0) draw_line(x,oY,x+1,Y,color,opacity,pattern,init_hatch);
27862           init_hatch = false;
27863           oY = Y;
27864         }
27865       } break;
27866       case 3 : { // Bars
27867         const int Y0 = (int)(-m/ca);
27868         int oX = 0;
27869         cimg_foroff(data,off) {
27870           const int
27871             X = (off+1)*_width/siz-1,
27872             Y = (int)((data[off]-m)/ca);
27873           draw_rectangle(oX,Y0,X,Y,color1,opacity).
27874             draw_line(oX,Y,oX,Y0,color2,opacity).
27875             draw_line(oX,Y0,X,Y0,Y<=Y0?color2:color,opacity).
27876             draw_line(X,Y,X,Y0,color,opacity).
27877             draw_line(oX,Y,X,Y,Y<=Y0?color:color2,opacity);
27878           oX = X+1;
27879         }
27880       } break;
27881       default : break; // No edges
27882       }
27883 
27884       // Draw graph points
27885       switch (vertex_type%8) {
27886       case 1 : { // Point
27887         cimg_foroff(data,off) {
27888           const int X = off*_width/(siz-xp), Y = (int)((data[off]-m)/ca);
27889           draw_point(X,Y,color,opacity);
27890         }
27891       } break;
27892       case 2 : { // Straight Cross
27893         cimg_foroff(data,off) {
27894           const int X = off*_width/(siz-xp), Y = (int)((data[off]-m)/ca);
27895           draw_line(X-3,Y,X+3,Y,color,opacity).draw_line(X,Y-3,X,Y+3,color,opacity);
27896         }
27897       } break;
27898       case 3 : { // Diagonal Cross
27899         cimg_foroff(data,off) {
27900           const int X = off*_width/(siz-xp), Y = (int)((data[off]-m)/ca);
27901           draw_line(X-3,Y-3,X+3,Y+3,color,opacity).draw_line(X-3,Y+3,X+3,Y-3,color,opacity);
27902         }
27903       } break;
27904       case 4 : { // Filled Circle
27905         cimg_foroff(data,off) {
27906           const int X = off*_width/(siz-xp), Y = (int)((data[off]-m)/ca);
27907           draw_circle(X,Y,3,color,opacity);
27908         }
27909       } break;
27910       case 5 : { // Outlined circle
27911         cimg_foroff(data,off) {
27912           const int X = off*_width/(siz-xp), Y = (int)((data[off]-m)/ca);
27913           draw_circle(X,Y,3,color,opacity,0U);
27914         }
27915       } break;
27916       case 6 : { // Square
27917         cimg_foroff(data,off) {
27918           const int X = off*_width/(siz-xp), Y = (int)((data[off]-m)/ca);
27919           draw_rectangle(X-3,Y-3,X+3,Y+3,color,opacity,~0U);
27920         }
27921       } break;
27922       case 7 : { // Diamond
27923         cimg_foroff(data,off) {
27924           const int X = off*_width/(siz-xp), Y = (int)((data[off]-m)/ca);
27925           draw_line(X,Y-4,X+4,Y,color,opacity).
27926             draw_line(X+4,Y,X,Y+4,color,opacity).
27927             draw_line(X,Y+4,X-4,Y,color,opacity).
27928             draw_line(X-4,Y,X,Y-4,color,opacity);
27929         }
27930       } break;
27931       default : break; // No points
27932       }
27933 
27934       if (color1) delete[] color1; if (color2) delete[] color2;
27935       return *this;
27936     }
27937 
27938     //! Draw a 3d filled region starting from a point (\c x,\c y,\ z) in the instance image.
27939     /**
27940        \param x X-coordinate of the starting point of the region to fill.
27941        \param y Y-coordinate of the starting point of the region to fill.
27942        \param z Z-coordinate of the starting point of the region to fill.
27943        \param color An array of spectrum() values of type \c T, defining the drawing color.
27944        \param region Image that will contain the mask of the filled region mask, as an output.
27945        \param sigma Tolerance concerning neighborhood values.
27946        \param opacity Opacity of the drawing.
27947        \param high_connexity Tells if 8-connexity must be used (only for 2d images).
27948        \return \p region is initialized with the binary mask of the filled region.
27949     **/
27950     template<typename tc, typename t>
27951     CImg<T>& draw_fill(const int x, const int y, const int z,
27952                        const tc *const color, const float opacity,
27953                        CImg<t>& region, const float sigma=0,
27954                        const bool high_connexity=false) {
27955 
27956 #define _cimg_draw_fill_test(x,y,z,res) if (region(x,y,z)) res = false; else { \
27957   res = true; \
27958   const T *reference_col = reference_color._data + _spectrum, *ptrs = data(x,y,z) + siz; \
27959   for (unsigned int i = _spectrum; res && i; --i) { ptrs-=whd; res = (cimg::abs(*ptrs - *(--reference_col))<=sigma); } \
27960   region(x,y,z) = (t)(res?1:noregion); \
27961 }
27962 
27963 #define _cimg_draw_fill_set(x,y,z) { \
27964   const tc *col = color; \
27965   T *ptrd = data(x,y,z); \
27966   if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } \
27967   else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } \
27968 }
27969 
27970 #define _cimg_draw_fill_insert(x,y,z) { \
27971   if (posr1>=remaining._height) remaining.resize(3,remaining._height<<1,1,1,0); \
27972   unsigned int *ptrr = remaining.data(0,posr1); \
27973   *(ptrr++) = x; *(ptrr++) = y; *(ptrr++) = z; ++posr1; \
27974 }
27975 
27976 #define _cimg_draw_fill_test_neighbor(x,y,z,cond) if (cond) { \
27977   const unsigned int tx = x, ty = y, tz = z; \
27978   _cimg_draw_fill_test(tx,ty,tz,res); if (res) _cimg_draw_fill_insert(tx,ty,tz); \
27979 }
27980 
27981       if (!color)
27982         throw CImgArgumentException(_cimg_instance
27983                                     "draw_fill() : Specified color is (null).",
27984                                     cimg_instance);
27985 
27986       region.assign(_width,_height,_depth,1,(t)0);
27987       if (x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth()) {
27988         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27989         const unsigned int whd = _width*_height*_depth, siz = _spectrum*whd, W1 = _width-1, H1 = _height-1, D1 = _depth-1;
27990         const bool is_threed = (_depth>1);
27991         const CImg<T> reference_color = get_vector_at(x,y,z);
27992         CImg<uintT> remaining(3,512,1,1,0);
27993         remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z;
27994         unsigned int posr0 = 0, posr1 = 1;
27995         region(x,y,z) = (t)1;
27996         const t noregion = ((t)1==(t)2)?(t)0:(t)(-1);
27997         if (is_threed) do { // 3d version of the filling algorithm
27998           const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++);
27999           if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
28000           bool cont, res;
28001           unsigned int nxc = xc;
28002           do { // X-backward
28003             _cimg_draw_fill_set(nxc,yc,zc);
28004             _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
28005             _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
28006             _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
28007             _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
28008             if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
28009           } while (cont);
28010           nxc = xc;
28011           do { // X-forward
28012             if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
28013             if (cont) {
28014               _cimg_draw_fill_set(nxc,yc,zc);
28015               _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
28016               _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
28017               _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
28018               _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
28019             }
28020           } while (cont);
28021           unsigned int nyc = yc;
28022           do { // Y-backward
28023             if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
28024             if (cont) {
28025               _cimg_draw_fill_set(xc,nyc,zc);
28026               _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
28027               _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
28028               _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
28029               _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
28030             }
28031           } while (cont);
28032           nyc = yc;
28033           do { // Y-forward
28034             if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
28035             if (cont) {
28036               _cimg_draw_fill_set(xc,nyc,zc);
28037               _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
28038               _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
28039               _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
28040               _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
28041             }
28042           } while (cont);
28043           unsigned int nzc = zc;
28044           do { // Z-backward
28045             if (nzc) { --nzc; _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
28046             if (cont) {
28047               _cimg_draw_fill_set(xc,yc,nzc);
28048               _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
28049               _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
28050               _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
28051               _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
28052             }
28053           } while (cont);
28054           nzc = zc;
28055           do { // Z-forward
28056             if ((++nzc)<=D1) { _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
28057             if (cont) {
28058               _cimg_draw_fill_set(xc,nyc,zc);
28059               _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
28060               _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
28061               _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
28062               _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
28063             }
28064           } while (cont);
28065         } while (posr1>posr0);
28066         else do { // 2d version of the filling algorithm
28067           const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++);
28068           if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
28069           bool cont, res;
28070           unsigned int nxc = xc;
28071           do { // X-backward
28072             _cimg_draw_fill_set(nxc,yc,0);
28073             _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
28074             _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
28075             if (high_connexity) {
28076               _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
28077               _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
28078               _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
28079               _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
28080             }
28081             if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
28082           } while (cont);
28083           nxc = xc;
28084           do { // X-forward
28085             if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
28086             if (cont) {
28087               _cimg_draw_fill_set(nxc,yc,0);
28088               _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
28089               _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
28090               if (high_connexity) {
28091                 _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
28092                 _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
28093                 _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
28094                 _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
28095               }
28096             }
28097           } while (cont);
28098           unsigned int nyc = yc;
28099           do { // Y-backward
28100             if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
28101             if (cont) {
28102               _cimg_draw_fill_set(xc,nyc,0);
28103               _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
28104               _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
28105               if (high_connexity) {
28106                 _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
28107                 _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
28108                 _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
28109                 _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
28110               }
28111             }
28112           } while (cont);
28113           nyc = yc;
28114           do { // Y-forward
28115             if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
28116             if (cont) {
28117               _cimg_draw_fill_set(xc,nyc,0);
28118               _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
28119               _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
28120               if (high_connexity) {
28121                 _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
28122                 _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
28123                 _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
28124                 _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
28125               }
28126             }
28127           } while (cont);
28128         } while (posr1>posr0);
28129         if (noregion) cimg_for(region,ptrd,t) if (*ptrd==noregion) *ptrd = (t)0;
28130       }
28131       return *this;
28132     }
28133 
28134     //! Draw a 3d filled region starting from a point (\c x,\c y,\ z) in the instance image.
28135     /**
28136        \param x = X-coordinate of the starting point of the region to fill.
28137        \param y = Y-coordinate of the starting point of the region to fill.
28138        \param z = Z-coordinate of the starting point of the region to fill.
28139        \param color = an array of spectrum() values of type \c T, defining the drawing color.
28140        \param sigma = tolerance concerning neighborhood values.
28141        \param opacity = opacity of the drawing.
28142     **/
28143     template<typename tc>
28144     CImg<T>& draw_fill(const int x, const int y, const int z,
28145                        const tc *const color, const float opacity=1,
28146                        const float sigma=0, const bool high_connexity=false) {
28147       CImg<boolT> tmp;
28148       return draw_fill(x,y,z,color,opacity,tmp,sigma,high_connexity);
28149     }
28150 
28151     //! Draw a 2d filled region starting from a point (\c x,\c y) in the instance image.
28152     /**
28153        \param x = X-coordinate of the starting point of the region to fill.
28154        \param y = Y-coordinate of the starting point of the region to fill.
28155        \param color = an array of spectrum() values of type \c T, defining the drawing color.
28156        \param sigma = tolerance concerning neighborhood values.
28157        \param opacity = opacity of the drawing.
28158     **/
28159     template<typename tc>
28160     CImg<T>& draw_fill(const int x, const int y,
28161                        const tc *const color, const float opacity=1,
28162                        const float sigma=0, const bool high_connexity=false) {
28163       CImg<boolT> tmp;
28164       return draw_fill(x,y,0,color,opacity,tmp,sigma,high_connexity);
28165     }
28166 
28167     //! Draw a plasma random texture.
28168     /**
28169        \param x0 = X-coordinate of the upper-left corner of the plasma.
28170        \param y0 = Y-coordinate of the upper-left corner of the plasma.
28171        \param x1 = X-coordinate of the lower-right corner of the plasma.
28172        \param y1 = Y-coordinate of the lower-right corner of the plasma.
28173        \param alpha = Alpha-parameter of the plasma.
28174        \param beta = Beta-parameter of the plasma.
28175        \param opacity = opacity of the drawing.
28176     **/
28177     CImg<T>& draw_plasma(const int x0, const int y0, const int x1, const int y1,
28178                          const float alpha=1, const float beta=1,
28179                          const float opacity=1) {
28180       if (!is_empty()) {
28181         const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28182         int nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1;
28183         if (nx1<nx0) cimg::swap(nx0,nx1);
28184         if (ny1<ny0) cimg::swap(ny0,ny1);
28185         if (nx0<0) nx0 = 0;
28186         if (nx1>=width()) nx1 = _width-1;
28187         if (ny0<0) ny0 = 0;
28188         if (ny1>=height()) ny1 = _height-1;
28189         const int xc = (nx0+nx1)/2, yc = (ny0+ny1)/2, dx = (xc-nx0), dy = (yc-ny0);
28190         const Tfloat dc = (Tfloat)(std::sqrt((float)(dx*dx+dy*dy))*alpha + beta);
28191         Tfloat val = 0;
28192         cimg_forC(*this,c) {
28193           if (opacity>=1) {
28194             const Tfloat
28195               val0 = (Tfloat)((*this)(nx0,ny0,0,c)), val1 = (Tfloat)((*this)(nx1,ny0,0,c)),
28196               val2 = (Tfloat)((*this)(nx0,ny1,0,c)), val3 = (Tfloat)((*this)(nx1,ny1,0,c));
28197             (*this)(xc,ny0,0,c) = (T)((val0+val1)/2);
28198             (*this)(xc,ny1,0,c) = (T)((val2+val3)/2);
28199             (*this)(nx0,yc,0,c) = (T)((val0+val2)/2);
28200             (*this)(nx1,yc,0,c) = (T)((val1+val3)/2);
28201             do {
28202               val = (Tfloat)(0.25f*((Tfloat)((*this)(nx0,ny0,0,c)) +
28203                                     (Tfloat)((*this)(nx1,ny0,0,c)) +
28204                                     (Tfloat)((*this)(nx1,ny1,0,c)) +
28205                                     (Tfloat)((*this)(nx0,ny1,0,c))) +
28206                              dc*cimg::grand());
28207             } while (val<(Tfloat)cimg::type<T>::min() || val>(Tfloat)cimg::type<T>::max());
28208             (*this)(xc,yc,0,c) = (T)val;
28209           } else {
28210             const Tfloat
28211               val0 = (Tfloat)((*this)(nx0,ny0,0,c)), val1 = (Tfloat)((*this)(nx1,ny0,0,c)),
28212               val2 = (Tfloat)((*this)(nx0,ny1,0,c)), val3 = (Tfloat)((*this)(nx1,ny1,0,c));
28213             (*this)(xc,ny0,0,c) = (T)(((val0+val1)*nopacity + copacity*(*this)(xc,ny0,0,c))/2);
28214             (*this)(xc,ny1,0,c) = (T)(((val2+val3)*nopacity + copacity*(*this)(xc,ny1,0,c))/2);
28215             (*this)(nx0,yc,0,c) = (T)(((val0+val2)*nopacity + copacity*(*this)(nx0,yc,0,c))/2);
28216             (*this)(nx1,yc,0,c) = (T)(((val1+val3)*nopacity + copacity*(*this)(nx1,yc,0,c))/2);
28217             do {
28218               val = (Tfloat)(0.25f*(((Tfloat)((*this)(nx0,ny0,0,c)) +
28219                                      (Tfloat)((*this)(nx1,ny0,0,c)) +
28220                                      (Tfloat)((*this)(nx1,ny1,0,c)) +
28221                                      (Tfloat)((*this)(nx0,ny1,0,c))) +
28222                                     dc*cimg::grand())*nopacity + copacity*(*this)(xc,yc,0,c));
28223             } while (val<(Tfloat)cimg::type<T>::min() || val>(Tfloat)cimg::type<T>::max());
28224             (*this)(xc,yc,0,c) = (T)val;
28225           }
28226         }
28227         if (xc!=nx0 || yc!=ny0) {
28228           draw_plasma(nx0,ny0,xc,yc,alpha,beta,opacity);
28229           draw_plasma(xc,ny0,nx1,yc,alpha,beta,opacity);
28230           draw_plasma(nx0,yc,xc,ny1,alpha,beta,opacity);
28231           draw_plasma(xc,yc,nx1,ny1,alpha,beta,opacity);
28232         }
28233       }
28234       return *this;
28235     }
28236 
28237     //! Draw a plasma random texture.
28238     /**
28239        \param alpha = Alpha-parameter of the plasma.
28240        \param beta = Beta-parameter of the plasma.
28241        \param opacity = opacity of the drawing.
28242     **/
28243     CImg<T>& draw_plasma(const float alpha=1, const float beta=1,
28244                          const float opacity=1) {
28245       return draw_plasma(0,0,_width-1,_height-1,alpha,beta,opacity);
28246     }
28247 
28248     //! Draw a quadratic Mandelbrot or Julia fractal set, computed using the Escape Time Algorithm.
28249     template<typename tc>
28250     CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
28251                              const CImg<tc>& color_palette, const float opacity=1,
28252                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
28253                              const unsigned int iteration_max=255,
28254                              const bool normalized_iteration=false,
28255                              const bool julia_set=false,
28256                              const double paramr=0, const double parami=0) {
28257       if (is_empty()) return *this;
28258       CImg<tc> palette;
28259       if (color_palette) palette.assign(color_palette._data,color_palette.size()/color_palette._spectrum,1,1,color_palette._spectrum,true);
28260       if (palette && palette._spectrum!=_spectrum)
28261         throw CImgArgumentException(_cimg_instance
28262                                     "draw_mandelbrot() : Instance and specified color palette (%u,%u,%u,%u,%p) have incompatible dimensions.",
28263                                     cimg_instance,
28264                                     color_palette._width,color_palette._height,color_palette._depth,color_palette._spectrum,color_palette._data);
28265 
28266       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), ln2 = (float)std::log(2.0);
28267       unsigned int iteration = 0;
28268       cimg_for_inXY(*this,x0,y0,x1,y1,p,q) {
28269         const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
28270         double zr, zi, cr, ci;
28271         if (julia_set) { zr = x; zi = y; cr = paramr; ci = parami; }
28272         else { zr = paramr; zi = parami; cr = x; ci = y; }
28273         for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
28274           const double temp = zr*zr - zi*zi + cr;
28275           zi = 2*zr*zi + ci;
28276           zr = temp;
28277         }
28278         if (iteration>iteration_max) {
28279           if (palette) {
28280             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
28281             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
28282           } else {
28283             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
28284             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
28285           }
28286         } else if (normalized_iteration) {
28287           const float
28288             normz = (float)cimg::abs(zr*zr+zi*zi),
28289             niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
28290           if (palette) {
28291             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
28292             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
28293           } else {
28294             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
28295             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
28296           }
28297         } else {
28298           if (palette) {
28299             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
28300             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
28301           } else {
28302             if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
28303             else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
28304           }
28305         }
28306       }
28307       return *this;
28308     }
28309 
28310     //! Draw a quadratic Mandelbrot or Julia fractal set, computed using the Escape Time Algorithm.
28311     template<typename tc>
28312     CImg<T>& draw_mandelbrot(const CImg<tc>& color_palette, const float opacity=1,
28313                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
28314                              const unsigned int iteration_max=255,
28315                              const bool normalized_iteration=false,
28316                              const bool julia_set=false,
28317                              const double paramr=0, const double parami=0) {
28318       return draw_mandelbrot(0,0,_width-1,_height-1,color_palette,opacity,
28319                              z0r,z0i,z1r,z1i,iteration_max,normalized_iteration,julia_set,paramr,parami);
28320     }
28321 
28322     //! Draw a 1d gaussian function in the instance image.
28323     /**
28324        \param xc = X-coordinate of the gaussian center.
28325        \param sigma = Standard variation of the gaussian distribution.
28326        \param color = array of spectrum() values of type \c T, defining the drawing color.
28327        \param opacity = opacity of the drawing.
28328     **/
28329     template<typename tc>
28330     CImg<T>& draw_gaussian(const float xc, const float sigma,
28331                            const tc *const color, const float opacity=1) {
28332       if (!color)
28333         throw CImgArgumentException(_cimg_instance
28334                                     "draw_gaussian() : Specified color is (null).",
28335                                     cimg_instance);
28336 
28337       if (is_empty()) return *this;
28338       const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28339       const unsigned int whd = _width*_height*_depth;
28340       const tc *col = color;
28341       cimg_forX(*this,x) {
28342         const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
28343         T *ptrd = data(x,0,0,0);
28344         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
28345         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
28346         col-=_spectrum;
28347       }
28348       return *this;
28349     }
28350 
28351     //! Draw an anisotropic 2d gaussian function.
28352     /**
28353        \param xc = X-coordinate of the gaussian center.
28354        \param yc = Y-coordinate of the gaussian center.
28355        \param tensor = 2x2 covariance matrix.
28356        \param color = array of spectrum() values of type \c T, defining the drawing color.
28357        \param opacity = opacity of the drawing.
28358     **/
28359     template<typename t, typename tc>
28360     CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
28361                            const tc *const color, const float opacity=1) {
28362       if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
28363         throw CImgArgumentException(_cimg_instance
28364                                     "draw_gaussian() : Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
28365                                     cimg_instance,
28366                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
28367       if (!color)
28368         throw CImgArgumentException(_cimg_instance
28369                                     "draw_gaussian() : Specified color is (null).",
28370                                     cimg_instance);
28371 
28372       if (is_empty()) return *this;
28373       typedef typename CImg<t>::Tfloat tfloat;
28374       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
28375       const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
28376       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28377       const unsigned int whd = _width*_height*_depth;
28378       const tc *col = color;
28379       float dy = -yc;
28380       cimg_forY(*this,y) {
28381         float dx = -xc;
28382         cimg_forX(*this,x) {
28383           const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
28384           T *ptrd = data(x,y,0,0);
28385           if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
28386           else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
28387           col-=_spectrum;
28388           ++dx;
28389         }
28390         ++dy;
28391       }
28392       return *this;
28393     }
28394 
28395     //! Draw an anisotropic 2d gaussian function.
28396     template<typename tc>
28397     CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
28398                            const tc *const color, const float opacity=1) {
28399       const double
28400         a = r1*ru*ru + r2*rv*rv,
28401         b = (r1-r2)*ru*rv,
28402         c = r1*rv*rv + r2*ru*ru;
28403       const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
28404       return draw_gaussian(xc,yc,tensor,color,opacity);
28405     }
28406 
28407     //! Draw an isotropic 2d gaussian function.
28408     /**
28409        \param xc = X-coordinate of the gaussian center.
28410        \param yc = Y-coordinate of the gaussian center.
28411        \param sigma = standard variation of the gaussian distribution.
28412        \param color = array of spectrum() values of type \c T, defining the drawing color.
28413        \param opacity = opacity of the drawing.
28414     **/
28415     template<typename tc>
28416     CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
28417                            const tc *const color, const float opacity=1) {
28418       return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
28419     }
28420 
28421     //! Draw an anisotropic 3d gaussian function.
28422     /**
28423        \param xc = X-coordinate of the gaussian center.
28424        \param yc = Y-coordinate of the gaussian center.
28425        \param zc = Z-coordinate of the gaussian center.
28426        \param tensor = 3x3 covariance matrix.
28427        \param color = array of spectrum() values of type \c T, defining the drawing color.
28428        \param opacity = opacity of the drawing.
28429     **/
28430     template<typename t, typename tc>
28431     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
28432                            const tc *const color, const float opacity=1) {
28433       if (is_empty()) return *this;
28434       typedef typename CImg<t>::Tfloat tfloat;
28435       if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
28436         throw CImgArgumentException(_cimg_instance
28437                                     "draw_gaussian() : Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
28438                                     cimg_instance,
28439                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
28440 
28441       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
28442       const tfloat a = invT(0,0), b = 2*invT(1,0), c = 2*invT(2,0), d = invT(1,1), e = 2*invT(2,1), f = invT(2,2);
28443       const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28444       const unsigned int whd = _width*_height*_depth;
28445       const tc *col = color;
28446       cimg_forXYZ(*this,x,y,z) {
28447         const float
28448           dx = (x - xc), dy = (y - yc), dz = (z - zc),
28449           val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
28450         T *ptrd = data(x,y,z,0);
28451         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
28452         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
28453         col-=_spectrum;
28454       }
28455       return *this;
28456     }
28457 
28458     //! Draw an isotropic 3d gaussian function.
28459    /**
28460        \param xc = X-coordinate of the gaussian center.
28461        \param yc = Y-coordinate of the gaussian center.
28462        \param zc = Z-coordinate of the gaussian center.
28463        \param sigma = standard variation of the gaussian distribution.
28464        \param color = array of spectrum() values of type \c T, defining the drawing color.
28465        \param opacity = opacity of the drawing.
28466     **/
28467     template<typename tc>
28468     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
28469                            const tc *const color, const float opacity=1) {
28470       return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
28471     }
28472 
28473     //! Draw a 3d object.
28474     /**
28475        \param X = X-coordinate of the 3d object position
28476        \param Y = Y-coordinate of the 3d object position
28477        \param Z = Z-coordinate of the 3d object position
28478        \param vertices = Image Nx3 describing 3d point coordinates
28479        \param primitives = List of P primitives
28480        \param colors = List of P color (or textures)
28481        \param opacities = Image or list of P opacities
28482        \param render_type = Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
28483        \param double_sided = Tell if object faces have two sides or are oriented.
28484        \param focale = length of the focale
28485        \param lightx = X-coordinate of the light
28486        \param lighty = Y-coordinate of the light
28487        \param lightz = Z-coordinate of the light
28488        \param specular_shine = Shininess of the object
28489     **/
28490     template<typename tp, typename tf, typename tc, typename to>
28491     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
28492                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
28493                            const CImgList<tc>& colors, const CImg<to>& opacities,
28494                            const unsigned int render_type=4,
28495                            const bool double_sided=false, const float focale=500,
28496                            const float lightx=0, const float lighty=0, const float lightz=-5000,
28497                            const float specular_light=0.2f, const float specular_shine=0.1f,
28498                            CImg<floatT>& zbuffer=cimg_library::CImg<floatT>::empty()) {
28499       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
28500                             render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine);
28501     }
28502 
28503 #ifdef cimg_use_board
28504     template<typename tp, typename tf, typename tc, typename to>
28505     CImg<T>& draw_object3d(LibBoard::Board& board,
28506                            const float x0, const float y0, const float z0,
28507                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
28508                            const CImgList<tc>& colors, const CImg<to>& opacities,
28509                            const unsigned int render_type=4,
28510                            const bool double_sided=false, const float focale=500,
28511                            const float lightx=0, const float lighty=0, const float lightz=-5000,
28512                            const float specular_light=0.2f, const float specular_shine=0.1f,
28513                            CImg<floatT>& zbuffer=cimg_library::CImg<floatT>::empty()) {
28514       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
28515                             render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine);
28516     }
28517 #endif
28518 
28519     template<typename tp, typename tf, typename tc, typename to>
28520     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
28521                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
28522                            const CImgList<tc>& colors, const CImgList<to>& opacities,
28523                            const unsigned int render_type=4,
28524                            const bool double_sided=false, const float focale=500,
28525                            const float lightx=0, const float lighty=0, const float lightz=-5000,
28526                            const float specular_light=0.2f, const float specular_shine=0.1f,
28527                            CImg<floatT>& zbuffer=cimg_library::CImg<floatT>::empty()) {
28528       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
28529                             render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine);
28530     }
28531 
28532 #ifdef cimg_use_board
28533     template<typename tp, typename tf, typename tc, typename to>
28534     CImg<T>& draw_object3d(LibBoard::Board& board,
28535                            const float x0, const float y0, const float z0,
28536                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
28537                            const CImgList<tc>& colors, const CImgList<to>& opacities,
28538                            const unsigned int render_type=4,
28539                            const bool double_sided=false, const float focale=500,
28540                            const float lightx=0, const float lighty=0, const float lightz=-5000,
28541                            const float specular_light=0.2f, const float specular_shine=0.1f,
28542                            CImg<floatT>& zbuffer=cimg_library::CImg<floatT>::empty()) {
28543       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
28544                             render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine);
28545     }
28546 #endif
28547 
28548     //! Draw a 3d object.
28549     template<typename tp, typename tf, typename tc>
28550     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
28551                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
28552                            const CImgList<tc>& colors,
28553                            const unsigned int render_type=4,
28554                            const bool double_sided=false, const float focale=500,
28555                            const float lightx=0, const float lighty=0, const float lightz=-5000,
28556                            const float specular_light=0.2f, const float specular_shine=0.1f,
28557                            CImg<floatT>& zbuffer=cimg_library::CImg<floatT>::empty()) {
28558       static const CImg<floatT> opacities;
28559       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,
28560                            render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
28561     }
28562 
28563 #ifdef cimg_use_board
28564     template<typename tp, typename tf, typename tc, typename to>
28565     CImg<T>& draw_object3d(LibBoard::Board& board,
28566                            const float x0, const float y0, const float z0,
28567                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
28568                            const CImgList<tc>& colors,
28569                            const unsigned int render_type=4,
28570                            const bool double_sided=false, const float focale=500,
28571                            const float lightx=0, const float lighty=0, const float lightz=-5000,
28572                            const float specular_light=0.2f, const float specular_shine=0.1f,
28573                            CImg<floatT>& zbuffer=cimg_library::CImg<floatT>::empty()) {
28574       static const CImg<floatT> opacities;
28575       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,
28576                            render_type,double_sided,focale,lightx,lighty,lightz,specular_light,specular_shine,zbuffer);
28577     }
28578 #endif
28579 
28580     template<typename tc, typename to>
28581     void __draw_object3d(const unsigned int n_primitive, const CImgList<to>& opacities, const CImg<tc>& color,
28582                          const int nx0, const int ny0, const CImg<T>& sprite, const float opac) {
28583       if (n_primitive<opacities._width && opacities[n_primitive].is_sameXY(color))
28584         draw_image(nx0,ny0,sprite,opacities[n_primitive].get_resize(sprite._width,sprite._height,1,sprite._spectrum,1));
28585       else draw_image(nx0,ny0,sprite,opac);
28586     }
28587 
28588     template<typename tc, typename to>
28589     void __draw_object3d(const unsigned int, const CImg<to>&, const CImg<tc>&,
28590                          const int nx0, const int ny0, const CImg<T>& sprite, const float opac) {
28591       draw_image(nx0,ny0,sprite,opac);
28592     }
28593 
28594     template<typename tp, typename tf, typename tc, typename to>
28595     CImg<T>& _draw_object3d(void *const pboard, CImg<floatT>& zbuffer,
28596                             const float X, const float Y, const float Z,
28597                             const CImg<tp>& vertices,
28598                             const CImgList<tf>& primitives,
28599                             const CImgList<tc>& colors,
28600                             const to& opacities,
28601                             const unsigned int render_type,
28602                             const bool double_sided, const float focale,
28603                             const float lightx, const float lighty, const float lightz,
28604                             const float specular_light, const float specular_shine) {
28605       if (is_empty() || !vertices || !primitives) return *this;
28606       char error_message[1024] = { 0 };
28607       if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
28608         throw CImgArgumentException(_cimg_instance
28609                                     "draw_object3d() : Invalid specified 3d object (%u,%u) (%s).",
28610                                     cimg_instance,vertices._width,primitives._width,error_message);
28611 #ifndef cimg_use_board
28612       if (pboard) return *this;
28613 #endif
28614       const float
28615         nspec = 1 - (specular_light<0.0f?0.0f:(specular_light>1.0f?1.0f:specular_light)),
28616         nspec2 = 1 + (specular_shine<0.0f?0.0f:specular_shine),
28617         nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
28618         nsl2 = 1 - 2*nsl1*nspec,
28619         nsl3 = nspec2 - nsl1 - nsl2;
28620 
28621       // Create light texture for phong-like rendering.
28622       static CImg<floatT> light_texture;
28623       if (render_type==5) {
28624         if (colors._width>primitives._width) light_texture.assign(colors[primitives._width])/=255;
28625         else {
28626           static float olightx = 0, olighty = 0, olightz = 0, ospecular_shine = 0;
28627           if (!light_texture || lightx!=olightx || lighty!=olighty || lightz!=olightz || specular_shine!=ospecular_shine || light_texture._spectrum<_spectrum) {
28628             light_texture.assign(512,512);
28629             const float
28630               dlx = lightx - X,
28631               dly = lighty - Y,
28632               dlz = lightz - Z,
28633               nl = (float)std::sqrt(dlx*dlx+dly*dly+dlz*dlz),
28634               nlx = light_texture._width/2*(1+dlx/nl),
28635               nly = light_texture._height/2*(1+dly/nl),
28636               white[] = { 1 };
28637             light_texture.draw_gaussian(nlx,nly,light_texture._width/3.0f,white);
28638             cimg_forXY(light_texture,x,y) {
28639               const float factor = light_texture(x,y);
28640               if (factor>nspec) light_texture(x,y) = cimg::min(2,nsl1*factor*factor+nsl2*factor+nsl3);
28641             }
28642             light_texture.resize(-100,-100,1,cimg::max(4U,_spectrum));
28643             olightx = lightx;
28644             olighty = lighty;
28645             olightz = lightz;
28646             ospecular_shine = specular_shine;
28647           }
28648         }
28649       }
28650 
28651       // Compute 3d to 2d projection.
28652       CImg<floatT> projections(vertices._width,2);
28653       cimg_forX(projections,l) {
28654         const float
28655           x = (float)vertices(l,0),
28656           y = (float)vertices(l,1),
28657           z = (float)vertices(l,2);
28658         const float projectedz = z + Z + focale;
28659         projections(l,1) = Y + focale*y/projectedz;
28660         projections(l,0) = X + focale*x/projectedz;
28661       }
28662 
28663       // Compute and sort visible primitives.
28664       CImg<uintT> visibles(primitives._width);
28665       CImg<floatT> zrange(primitives._width);
28666       unsigned int nb_visibles = 0;
28667       const float zmin = 1.5f - focale;
28668       cimglist_for(primitives,l) {
28669         const CImg<tf>& primitive = primitives[l];
28670         switch (primitive.size()) {
28671 
28672         case 1 : { // Point
28673           const unsigned int i0 = (unsigned int)primitive(0);
28674           const float x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z + vertices(i0,2));
28675           if (z0>zmin && x0>=0 && x0<_width && y0>=0 && y0<_height) {
28676             visibles(nb_visibles) = (unsigned int)l;
28677             zrange(nb_visibles++) = z0;
28678           }
28679         } break;
28680         case 5 : { // Sphere
28681           const unsigned int
28682             i0 = (unsigned int)primitive(0),
28683             i1 = (unsigned int)primitive(1);
28684           const float
28685             x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z + vertices(i0,2)),
28686             x1 = projections(i1,0), y1 = projections(i1,1), z1 = (float)(Z + vertices(i1,2));
28687           float xm, xM, ym, yM;
28688           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
28689           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
28690           if (z0>zmin && z1>zmin && xM>=0 && xm<_width && yM>=0 && ym<_height) {
28691             visibles(nb_visibles) = (unsigned int)l;
28692             zrange(nb_visibles++) = 0.5f*(z0 + z1);
28693           }
28694         } break;
28695         case 2 : // Segment
28696         case 6 : {
28697           const unsigned int
28698             i0 = (unsigned int)primitive(0),
28699             i1 = (unsigned int)primitive(1);
28700           const float
28701             x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z + vertices(i0,2)),
28702             x1 = projections(i1,0), y1 = projections(i1,1), z1 = (float)(Z + vertices(i1,2));
28703           float xm, xM, ym, yM;
28704           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
28705           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
28706           if (z0>zmin && z1>zmin && xM>=0 && xm<_width && yM>=0 && ym<_height) {
28707             visibles(nb_visibles) = (unsigned int)l;
28708             zrange(nb_visibles++) = 0.5f*(z0 + z1);
28709           }
28710         } break;
28711         case 3 :  // Triangle
28712         case 9 : {
28713           const unsigned int
28714             i0 = (unsigned int)primitive(0),
28715             i1 = (unsigned int)primitive(1),
28716             i2 = (unsigned int)primitive(2);
28717           const float
28718             x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z + vertices(i0,2)),
28719             x1 = projections(i1,0), y1 = projections(i1,1), z1 = (float)(Z + vertices(i1,2)),
28720             x2 = projections(i2,0), y2 = projections(i2,1), z2 = (float)(Z + vertices(i2,2));
28721           float xm, xM, ym, yM;
28722           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
28723           if (x2<xm) xm = x2;
28724           if (x2>xM) xM = x2;
28725           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
28726           if (y2<ym) ym = y2;
28727           if (y2>yM) yM = y2;
28728           if (z0>zmin && z1>zmin && z2>zmin && xM>=0 && xm<_width && yM>=0 && ym<_height) {
28729             const float d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
28730             if (double_sided || d<0) {
28731               visibles(nb_visibles) = (unsigned int)l;
28732               zrange(nb_visibles++) = (z0 + z1 + z2)/3;
28733             }
28734           }
28735         } break;
28736         case 4 : // Rectangle
28737         case 12 : {
28738           const unsigned int
28739             i0 = (unsigned int)primitive(0),
28740             i1 = (unsigned int)primitive(1),
28741             i2 = (unsigned int)primitive(2),
28742             i3 = (unsigned int)primitive(3);
28743           const float
28744             x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z + vertices(i0,2)),
28745             x1 = projections(i1,0), y1 = projections(i1,1), z1 = (float)(Z + vertices(i1,2)),
28746             x2 = projections(i2,0), y2 = projections(i2,1), z2 = (float)(Z + vertices(i2,2)),
28747             x3 = projections(i3,0), y3 = projections(i3,1), z3 = (float)(Z + vertices(i3,2));
28748           float xm, xM, ym, yM;
28749           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
28750           if (x2<xm) xm = x2;
28751           if (x2>xM) xM = x2;
28752           if (x3<xm) xm = x3;
28753           if (x3>xM) xM = x3;
28754           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
28755           if (y2<ym) ym = y2;
28756           if (y2>yM) yM = y2;
28757           if (y3<ym) ym = y3;
28758           if (y3>yM) yM = y3;
28759           if (z0>zmin && z1>zmin && z2>zmin && z3>zmin && xM>=0 && xm<_width && yM>=0 && ym<_height) {
28760             const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
28761             if (double_sided || d<0) {
28762               visibles(nb_visibles) = (unsigned int)l;
28763               zrange(nb_visibles++) = (z0 + z1 + z2 + z3)/4;
28764             }
28765           }
28766         } break;
28767         default :
28768           throw CImgArgumentException(_cimg_instance
28769                                       "draw_object3d() : Invalid primitive[%u] with size %u "
28770                                       "(should have size 1,2,3,4,5,6,9 or 12).",
28771                                       cimg_instance,
28772                                       l,primitive.size());
28773         }
28774       }
28775       if (nb_visibles<=0) return *this;
28776       CImg<uintT> permutations;
28777       CImg<floatT>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,false);
28778 
28779       // Compute light properties
28780       CImg<floatT> lightprops;
28781       switch (render_type) {
28782       case 3 : { // Flat Shading
28783         lightprops.assign(nb_visibles);
28784         cimg_forX(lightprops,l) {
28785           const CImg<tf>& primitive = primitives(visibles(permutations(l)));
28786           const unsigned int psize = primitive.size();
28787           if (psize==3 || psize==4 || psize==9 || psize==12) {
28788             const unsigned int
28789               i0 = (unsigned int)primitive(0),
28790               i1 = (unsigned int)primitive(1),
28791               i2 = (unsigned int)primitive(2);
28792             const float
28793               x0 = (float)vertices(i0,0), y0 = (float)vertices(i0,1), z0 = (float)vertices(i0,2),
28794               x1 = (float)vertices(i1,0), y1 = (float)vertices(i1,1), z1 = (float)vertices(i1,2),
28795               x2 = (float)vertices(i2,0), y2 = (float)vertices(i2,1), z2 = (float)vertices(i2,2),
28796               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
28797               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
28798               nx = dy1*dz2 - dz1*dy2,
28799               ny = dz1*dx2 - dx1*dz2,
28800               nz = dx1*dy2 - dy1*dx2,
28801               norm = (float)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
28802               lx = X + (x0 + x1 + x2)/3 - lightx,
28803               ly = Y + (y0 + y1 + y2)/3 - lighty,
28804               lz = Z + (z0 + z1 + z2)/3 - lightz,
28805               nl = (float)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
28806               factor = cimg::max(cimg::abs(-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
28807             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
28808           } else lightprops[l] = 1;
28809         }
28810       } break;
28811 
28812       case 4 : // Gouraud Shading
28813       case 5 : { // Phong-Shading
28814         CImg<floatT> vertices_normals(vertices._width,3,1,1,0);
28815         for (unsigned int l = 0; l<nb_visibles; ++l) {
28816           const CImg<tf>& primitive = primitives[visibles(l)];
28817           const unsigned int psize = primitive.size();
28818           const bool
28819             triangle_flag = (psize==3) || (psize==9),
28820             rectangle_flag = (psize==4) || (psize==12);
28821           if (triangle_flag || rectangle_flag) {
28822             const unsigned int
28823               i0 = (unsigned int)primitive(0),
28824               i1 = (unsigned int)primitive(1),
28825               i2 = (unsigned int)primitive(2),
28826               i3 = rectangle_flag?(unsigned int)primitive(3):0;
28827             const float
28828               x0 = (float)vertices(i0,0), y0 = (float)vertices(i0,1), z0 = (float)vertices(i0,2),
28829               x1 = (float)vertices(i1,0), y1 = (float)vertices(i1,1), z1 = (float)vertices(i1,2),
28830               x2 = (float)vertices(i2,0), y2 = (float)vertices(i2,1), z2 = (float)vertices(i2,2),
28831               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
28832               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
28833               nnx = dy1*dz2 - dz1*dy2,
28834               nny = dz1*dx2 - dx1*dz2,
28835               nnz = dx1*dy2 - dy1*dx2,
28836               norm = 1e-5f + (float)std::sqrt(nnx*nnx + nny*nny + nnz*nnz),
28837               nx = nnx/norm,
28838               ny = nny/norm,
28839               nz = nnz/norm;
28840             vertices_normals(i0,0)+=nx; vertices_normals(i0,1)+=ny; vertices_normals(i0,2)+=nz;
28841             vertices_normals(i1,0)+=nx; vertices_normals(i1,1)+=ny; vertices_normals(i1,2)+=nz;
28842             vertices_normals(i2,0)+=nx; vertices_normals(i2,1)+=ny; vertices_normals(i2,2)+=nz;
28843             if (rectangle_flag) { vertices_normals(i3,0)+=nx; vertices_normals(i3,1)+=ny; vertices_normals(i3,2)+=nz; }
28844           }
28845         }
28846 
28847         if (double_sided) cimg_forX(vertices_normals,p) if (vertices_normals(p,2)>0) {
28848           vertices_normals(p,0) = -vertices_normals(p,0);
28849           vertices_normals(p,1) = -vertices_normals(p,1);
28850           vertices_normals(p,2) = -vertices_normals(p,2);
28851         }
28852 
28853         if (render_type==4) {
28854           lightprops.assign(vertices._width);
28855           cimg_forX(lightprops,ll) {
28856             const float
28857               nx = vertices_normals(ll,0),
28858               ny = vertices_normals(ll,1),
28859               nz = vertices_normals(ll,2),
28860               norm = (float)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
28861               lx = (float)(X + vertices(ll,0) - lightx),
28862               ly = (float)(Y + vertices(ll,1) - lighty),
28863               lz = (float)(Z + vertices(ll,2) - lightz),
28864               nl = (float)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
28865               factor = cimg::max((-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
28866             lightprops[ll] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
28867           }
28868         } else {
28869           const unsigned int
28870             lw2 = light_texture._width/2 - 1,
28871             lh2 = light_texture._height/2 - 1;
28872           lightprops.assign(vertices._width,2);
28873           cimg_forX(lightprops,ll) {
28874             const float
28875               nx = vertices_normals(ll,0),
28876               ny = vertices_normals(ll,1),
28877               nz = vertices_normals(ll,2),
28878               norm = (float)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
28879               nnx = nx/norm,
28880               nny = ny/norm;
28881             lightprops(ll,0) = lw2*(1 + nnx);
28882             lightprops(ll,1) = lh2*(1 + nny);
28883           }
28884         }
28885       } break;
28886       }
28887 
28888       // Draw visible primitives
28889       const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
28890       for (unsigned int l = 0; l<nb_visibles; ++l) {
28891         const unsigned int n_primitive = visibles(permutations(l));
28892         const CImg<tf>& primitive = primitives[n_primitive];
28893         const CImg<tc>& color = n_primitive<colors._width?colors[n_primitive]:default_color;
28894         const tc *const pcolor = color._data;
28895         const float opac = n_primitive<opacities.size()?opacities(n_primitive,0):1.0f;
28896 #ifdef cimg_use_board
28897         LibBoard::Board &board = *(LibBoard::Board*)pboard;
28898 #endif
28899 
28900         switch (primitive.size()) {
28901         case 1 : { // Colored point or sprite
28902           const unsigned int n0 = (unsigned int)primitive[0];
28903           const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1);
28904           if (color.size()==_spectrum) { // Colored point.
28905             draw_point(x0,y0,pcolor,opac);
28906 #ifdef cimg_use_board
28907             if (pboard) {
28908               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
28909               board.fillCircle((float)x0,height()-(float)y0,0);
28910             }
28911 #endif
28912           } else { // Colored sprite.
28913             const float z = Z + vertices(n0,2);
28914             const int
28915               factor = (int)(focale*100/(z+focale)),
28916               sw = color._width*factor/200,
28917               sh = color._height*factor/200,
28918               nx0 = x0 - sw, ny0 = y0 - sh;
28919             if (x0+sw>=0 && nx0<width() && y0+sh>=0 && ny0<height()) {
28920               const CImg<T> sprite = color.get_resize(-factor,-factor,1,-100,render_type<=3?1:3);
28921               __draw_object3d(n_primitive,opacities,color,nx0,ny0,sprite,opac);
28922 #ifdef cimg_use_board
28923               if (pboard) {
28924                 board.setPenColorRGBi(128,128,128);
28925                 board.setFillColor(LibBoard::Color::None);
28926                 board.drawRectangle((float)nx0,height()-(float)ny0,sw,sh);
28927               }
28928 #endif
28929             }
28930           }
28931         } break;
28932         case 2 : { // Colored line
28933           const unsigned int
28934             n0 = (unsigned int)primitive[0],
28935             n1 = (unsigned int)primitive[1];
28936           const int
28937             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
28938             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
28939           const float
28940             z0 = vertices(n0,2) + Z + focale,
28941             z1 = vertices(n1,2) + Z + focale;
28942           if (render_type) {
28943             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac);
28944             else draw_line(x0,y0,x1,y1,pcolor,opac);
28945 #ifdef cimg_use_board
28946             if (pboard) {
28947               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
28948               board.drawLine((float)x0,height()-(float)y0,x1,height()-(float)y1);
28949             }
28950 #endif
28951           } else {
28952             draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac);
28953 #ifdef cimg_use_board
28954             if (pboard) {
28955               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
28956               board.drawCircle((float)x0,height()-(float)y0,0);
28957               board.drawCircle((float)x1,height()-(float)y1,0);
28958             }
28959 #endif
28960           }
28961         } break;
28962         case 5 : { // Colored sphere
28963           const unsigned int
28964             n0 = (unsigned int)primitive[0],
28965             n1 = (unsigned int)primitive[1];
28966           const float
28967             Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)),
28968             Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)),
28969             Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)),
28970             zc = Z + Zc + focale,
28971             xc = X + focale*Xc/zc,
28972             yc = Y + focale*Yc/zc,
28973             radius = std::sqrt(cimg::sqr(Xc-vertices(n0,0)) + cimg::sqr(Yc-vertices(n0,1)) + cimg::sqr(Zc-vertices(n0,2)))*focale/zc;
28974           switch (render_type) {
28975           case 0 :
28976             draw_point((int)xc,(int)yc,pcolor,opac);
28977 #ifdef cimg_use_board
28978             if (pboard) {
28979               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
28980               board.fillCircle(xc,height()-yc,0);
28981             }
28982 #endif
28983             break;
28984           case 1 :
28985             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opac,~0U);
28986 #ifdef cimg_use_board
28987             if (pboard) {
28988               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
28989               board.setFillColor(LibBoard::Color::None);
28990               board.drawCircle(xc,height()-yc,radius);
28991             }
28992 #endif
28993             break;
28994           default :
28995             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opac);
28996 #ifdef cimg_use_board
28997             if (pboard) {
28998               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
28999               board.fillCircle(xc,height()-yc,radius);
29000             }
29001 #endif
29002             break;
29003           }
29004         } break;
29005         case 6 : { // Textured line
29006           const unsigned int
29007             n0 = (unsigned int)primitive[0],
29008             n1 = (unsigned int)primitive[1],
29009             tx0 = (unsigned int)primitive[2],
29010             ty0 = (unsigned int)primitive[3],
29011             tx1 = (unsigned int)primitive[4],
29012             ty1 = (unsigned int)primitive[5];
29013           const int
29014             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
29015             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
29016           const float
29017             z0 = vertices(n0,2) + Z + focale,
29018             z1 = vertices(n1,2) + Z + focale;
29019           if (render_type) {
29020             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac);
29021             else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac);
29022 #ifdef cimg_use_board
29023             if (pboard) {
29024               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29025               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
29026             }
29027 #endif
29028           } else {
29029             draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
29030               draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac);
29031 #ifdef cimg_use_board
29032             if (pboard) {
29033               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29034               board.drawCircle((float)x0,height()-(float)y0,0);
29035               board.drawCircle((float)x1,height()-(float)y1,0);
29036             }
29037 #endif
29038           }
29039         } break;
29040         case 3 : { // Colored triangle
29041           const unsigned int
29042             n0 = (unsigned int)primitive[0],
29043             n1 = (unsigned int)primitive[1],
29044             n2 = (unsigned int)primitive[2];
29045           const int
29046             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
29047             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
29048             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
29049           const float
29050             z0 = vertices(n0,2) + Z + focale,
29051             z1 = vertices(n1,2) + Z + focale,
29052             z2 = vertices(n2,2) + Z + focale;
29053           switch (render_type) {
29054           case 0 :
29055             draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac).draw_point(x2,y2,pcolor,opac);
29056 #ifdef cimg_use_board
29057             if (pboard) {
29058               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
29059               board.drawCircle((float)x0,height()-(float)y0,0);
29060               board.drawCircle((float)x1,height()-(float)y1,0);
29061               board.drawCircle((float)x2,height()-(float)y2,0);
29062             }
29063 #endif
29064             break;
29065           case 1 :
29066             if (zbuffer)
29067               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opac).
29068                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac);
29069             else
29070               draw_line(x0,y0,x1,y1,pcolor,opac).draw_line(x0,y0,x2,y2,pcolor,opac).
29071                 draw_line(x1,y1,x2,y2,pcolor,opac);
29072 #ifdef cimg_use_board
29073             if (pboard) {
29074               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
29075               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
29076               board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
29077               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29078             }
29079 #endif
29080             break;
29081           case 2 :
29082             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac);
29083             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac);
29084 #ifdef cimg_use_board
29085             if (pboard) {
29086               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
29087               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29088             }
29089 #endif
29090             break;
29091           case 3 :
29092             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac,lightprops(l));
29093             else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac,lightprops(l));
29094 #ifdef cimg_use_board
29095             if (pboard) {
29096               const float lp = cimg::min(lightprops(l),1);
29097               board.setPenColorRGBi((unsigned char)(color[0]*lp),
29098                                      (unsigned char)(color[1]*lp),
29099                                      (unsigned char)(color[2]*lp),
29100                                      (unsigned char)(opac*255));
29101               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29102             }
29103 #endif
29104             break;
29105           case 4 :
29106             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opac);
29107             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opac);
29108 #ifdef cimg_use_board
29109             if (pboard) {
29110               board.setPenColorRGBi((unsigned char)(color[0]),
29111                                      (unsigned char)(color[1]),
29112                                      (unsigned char)(color[2]),
29113                                      (unsigned char)(opac*255));
29114               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
29115                                          (float)x1,height()-(float)y1,lightprops(n1),
29116                                          (float)x2,height()-(float)y2,lightprops(n2));
29117             }
29118 #endif
29119             break;
29120           case 5 : {
29121             const unsigned int
29122               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
29123               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
29124               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1);
29125             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac);
29126             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac);
29127 #ifdef cimg_use_board
29128             if (pboard) {
29129               const float
29130                 l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
29131                 l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
29132                 l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1))));
29133               board.setPenColorRGBi((unsigned char)(color[0]),
29134                                      (unsigned char)(color[1]),
29135                                      (unsigned char)(color[2]),
29136                                      (unsigned char)(opac*255));
29137               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
29138                                          (float)x1,height()-(float)y1,l1,
29139                                          (float)x2,height()-(float)y2,l2);
29140             }
29141 #endif
29142           } break;
29143           }
29144         } break;
29145         case 4 : { // Colored rectangle
29146           const unsigned int
29147             n0 = (unsigned int)primitive[0],
29148             n1 = (unsigned int)primitive[1],
29149             n2 = (unsigned int)primitive[2],
29150             n3 = (unsigned int)primitive[3];
29151           const int
29152             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
29153             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
29154             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
29155             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
29156           const float
29157             z0 = vertices(n0,2) + Z + focale,
29158             z1 = vertices(n1,2) + Z + focale,
29159             z2 = vertices(n2,2) + Z + focale,
29160             z3 = vertices(n3,2) + Z + focale;
29161           switch (render_type) {
29162           case 0 :
29163             draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac).
29164               draw_point(x2,y2,pcolor,opac).draw_point(x3,y3,pcolor,opac);
29165 #ifdef cimg_use_board
29166             if (pboard) {
29167               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
29168               board.drawCircle((float)x0,height()-(float)y0,0);
29169               board.drawCircle((float)x1,height()-(float)y1,0);
29170               board.drawCircle((float)x2,height()-(float)y2,0);
29171               board.drawCircle((float)x3,height()-(float)y3,0);
29172             }
29173 #endif
29174             break;
29175           case 1 :
29176             if (zbuffer)
29177               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac).
29178                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opac).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opac);
29179             else
29180               draw_line(x0,y0,x1,y1,pcolor,opac).draw_line(x1,y1,x2,y2,pcolor,opac).
29181                 draw_line(x2,y2,x3,y3,pcolor,opac).draw_line(x3,y3,x0,y0,pcolor,opac);
29182 #ifdef cimg_use_board
29183             if (pboard) {
29184               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
29185               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
29186               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29187               board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
29188               board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
29189             }
29190 #endif
29191             break;
29192           case 2 :
29193             if (zbuffer)
29194               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac).draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opac);
29195             else
29196               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac);
29197 #ifdef cimg_use_board
29198             if (pboard) {
29199               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
29200               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29201               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
29202             }
29203 #endif
29204             break;
29205           case 3 :
29206             if (zbuffer)
29207               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac,lightprops(l)).
29208                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opac,lightprops(l));
29209             else
29210               _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac,lightprops(l)).
29211                 _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac,lightprops(l));
29212 #ifdef cimg_use_board
29213             if (pboard) {
29214               const float lp = cimg::min(lightprops(l),1);
29215               board.setPenColorRGBi((unsigned char)(color[0]*lp),
29216                                      (unsigned char)(color[1]*lp),
29217                                      (unsigned char)(color[2]*lp),(unsigned char)(opac*255));
29218               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29219               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
29220             }
29221 #endif
29222             break;
29223           case 4 : {
29224             const float
29225               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
29226               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
29227             if (zbuffer)
29228               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprop0,lightprop1,lightprop2,opac).
29229                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opac);
29230             else
29231               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opac).
29232                 draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opac);
29233 #ifdef cimg_use_board
29234             if (pboard) {
29235               board.setPenColorRGBi((unsigned char)(color[0]),
29236                                      (unsigned char)(color[1]),
29237                                      (unsigned char)(color[2]),
29238                                      (unsigned char)(opac*255));
29239               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
29240                                          (float)x1,height()-(float)y1,lightprop1,
29241                                          (float)x2,height()-(float)y2,lightprop2);
29242               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
29243                                          (float)x2,height()-(float)y2,lightprop2,
29244                                          (float)x3,height()-(float)y3,lightprop3);
29245             }
29246 #endif
29247           } break;
29248           case 5 : {
29249             const unsigned int
29250               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
29251               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
29252               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
29253               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
29254             if (zbuffer)
29255               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
29256                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
29257             else
29258               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
29259                 draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
29260 #ifdef cimg_use_board
29261             if (pboard) {
29262               const float
29263                 l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
29264                 l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
29265                 l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
29266                 l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
29267               board.setPenColorRGBi((unsigned char)(color[0]),
29268                                      (unsigned char)(color[1]),
29269                                      (unsigned char)(color[2]),
29270                                      (unsigned char)(opac*255));
29271               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
29272                                          (float)x1,height()-(float)y1,l1,
29273                                          (float)x2,height()-(float)y2,l2);
29274               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
29275                                          (float)x2,height()-(float)y2,l2,
29276                                          (float)x3,height()-(float)y3,l3);
29277             }
29278 #endif
29279           } break;
29280           }
29281         } break;
29282         case 9 : { // Textured triangle
29283           const unsigned int
29284             n0 = (unsigned int)primitive[0],
29285             n1 = (unsigned int)primitive[1],
29286             n2 = (unsigned int)primitive[2],
29287             tx0 = (unsigned int)primitive[3],
29288             ty0 = (unsigned int)primitive[4],
29289             tx1 = (unsigned int)primitive[5],
29290             ty1 = (unsigned int)primitive[6],
29291             tx2 = (unsigned int)primitive[7],
29292             ty2 = (unsigned int)primitive[8];
29293           const int
29294             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
29295             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
29296             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
29297           const float
29298             z0 = vertices(n0,2) + Z + focale,
29299             z1 = vertices(n1,2) + Z + focale,
29300             z2 = vertices(n2,2) + Z + focale;
29301           switch (render_type) {
29302           case 0 :
29303             draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
29304               draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac).
29305               draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac);
29306 #ifdef cimg_use_board
29307             if (pboard) {
29308               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29309               board.drawCircle((float)x0,height()-(float)y0,0);
29310               board.drawCircle((float)x1,height()-(float)y1,0);
29311               board.drawCircle((float)x2,height()-(float)y2,0);
29312             }
29313 #endif
29314             break;
29315           case 1 :
29316             if (zbuffer)
29317               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
29318                 draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac).
29319                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac);
29320             else
29321               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
29322                 draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac).
29323                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac);
29324 #ifdef cimg_use_board
29325             if (pboard) {
29326               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29327               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
29328               board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
29329               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29330             }
29331 #endif
29332             break;
29333           case 2 :
29334             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac);
29335             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac);
29336 #ifdef cimg_use_board
29337             if (pboard) {
29338               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29339               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29340             }
29341 #endif
29342             break;
29343           case 3 :
29344             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l));
29345             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l));
29346 #ifdef cimg_use_board
29347             if (pboard) {
29348               const float lp = cimg::min(lightprops(l),1);
29349               board.setPenColorRGBi((unsigned char)(128*lp),
29350                                      (unsigned char)(128*lp),
29351                                      (unsigned char)(128*lp),
29352                                      (unsigned char)(opac*255));
29353               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29354             }
29355 #endif
29356             break;
29357           case 4 :
29358             if (zbuffer)
29359               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac);
29360             else
29361               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac);
29362 #ifdef cimg_use_board
29363             if (pboard) {
29364               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29365               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
29366                                          (float)x1,height()-(float)y1,lightprops(n1),
29367                                          (float)x2,height()-(float)y2,lightprops(n2));
29368             }
29369 #endif
29370             break;
29371           case 5 :
29372             if (zbuffer)
29373               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
29374                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
29375                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
29376                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
29377                             opac);
29378             else
29379               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
29380                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
29381                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
29382                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
29383                             opac);
29384 #ifdef cimg_use_board
29385             if (pboard) {
29386               const float
29387                 l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
29388                 l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
29389                 l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1))));
29390               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29391               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,(float)x1,height()-(float)y1,l1,(float)x2,height()-(float)y2,l2);
29392             }
29393 #endif
29394             break;
29395           }
29396         } break;
29397         case 12 : { // Textured quadrangle
29398           const unsigned int
29399             n0 = (unsigned int)primitive[0],
29400             n1 = (unsigned int)primitive[1],
29401             n2 = (unsigned int)primitive[2],
29402             n3 = (unsigned int)primitive[3],
29403             tx0 = (unsigned int)primitive[4],
29404             ty0 = (unsigned int)primitive[5],
29405             tx1 = (unsigned int)primitive[6],
29406             ty1 = (unsigned int)primitive[7],
29407             tx2 = (unsigned int)primitive[8],
29408             ty2 = (unsigned int)primitive[9],
29409             tx3 = (unsigned int)primitive[10],
29410             ty3 = (unsigned int)primitive[11];
29411           const int
29412             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
29413             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
29414             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
29415             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
29416           const float
29417             z0 = vertices(n0,2) + Z + focale,
29418             z1 = vertices(n1,2) + Z + focale,
29419             z2 = vertices(n2,2) + Z + focale,
29420             z3 = vertices(n3,2) + Z + focale;
29421 
29422           switch (render_type) {
29423           case 0 :
29424             draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
29425               draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac).
29426               draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac).
29427               draw_point(x3,y3,color.get_vector_at(tx3,ty3)._data,opac);
29428 #ifdef cimg_use_board
29429             if (pboard) {
29430               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29431               board.drawCircle((float)x0,height()-(float)y0,0);
29432               board.drawCircle((float)x1,height()-(float)y1,0);
29433               board.drawCircle((float)x2,height()-(float)y2,0);
29434               board.drawCircle((float)x3,height()-(float)y3,0);
29435             }
29436 #endif
29437             break;
29438           case 1 :
29439             if (zbuffer)
29440               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
29441                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac).
29442                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac).
29443                 draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac);
29444             else
29445               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
29446                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac).
29447                 draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac).
29448                 draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac);
29449 #ifdef cimg_use_board
29450             if (pboard) {
29451               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29452               board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
29453               board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29454               board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
29455               board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
29456             }
29457 #endif
29458             break;
29459           case 2 :
29460             if (zbuffer)
29461               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac).
29462                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac);
29463             else
29464               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac).
29465                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac);
29466 #ifdef cimg_use_board
29467             if (pboard) {
29468               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29469               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29470               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
29471             }
29472 #endif
29473             break;
29474           case 3 :
29475             if (zbuffer)
29476               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)).
29477                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l));
29478             else
29479               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)).
29480                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l));
29481 #ifdef cimg_use_board
29482             if (pboard) {
29483               const float lp = cimg::min(lightprops(l),1);
29484               board.setPenColorRGBi((unsigned char)(128*lp),
29485                                      (unsigned char)(128*lp),
29486                                      (unsigned char)(128*lp),
29487                                      (unsigned char)(opac*255));
29488               board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
29489               board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
29490             }
29491 #endif
29492             break;
29493           case 4 : {
29494             const float
29495               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
29496               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
29497             if (zbuffer)
29498               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac).
29499                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac);
29500             else
29501               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac).
29502                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac);
29503 #ifdef cimg_use_board
29504             if (pboard) {
29505               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29506               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
29507                                          (float)x1,height()-(float)y1,lightprop1,
29508                                          (float)x2,height()-(float)y2,lightprop2);
29509               board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
29510                                          (float)x2,height()-(float)y2,lightprop2,
29511                                          (float)x3,height()-(float)y3,lightprop3);
29512             }
29513 #endif
29514           } break;
29515           case 5 : {
29516             const unsigned int
29517               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
29518               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
29519               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
29520               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
29521             if (zbuffer)
29522               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
29523                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
29524             else
29525               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
29526                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
29527 #ifdef cimg_use_board
29528             if (pboard) {
29529               const float
29530                 l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
29531                 l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
29532                 l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
29533                 l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
29534               board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
29535               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
29536                                          (float)x1,height()-(float)y1,l1,
29537                                          (float)x2,height()-(float)y2,l2);
29538               board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
29539                                          (float)x2,height()-(float)y2,l2,
29540                                          (float)x3,height()-(float)y3,l3);
29541             }
29542 #endif
29543           } break;
29544           }
29545         } break;
29546         }
29547       }
29548       return *this;
29549     }
29550 
29551     //@}
29552     //---------------------------
29553     //
29554     //! \name Data Input
29555     //@{
29556     //---------------------------
29557 
29558     //! Simple interface to select a shape from an image.
29559     /**
29560        \param selection  Array of 6 values containing the selection result
29561        \param coords_type Determine shape type to select (0=point, 1=vector, 2=rectangle, 3=circle)
29562        \param disp       Display window used to make the selection
29563        \param XYZ        Initial XYZ position (for volumetric images only)
29564        \param color      Color of the shape selector.
29565     **/
29566     CImg<T>& select(CImgDisplay &disp,
29567                     const int select_type=2, unsigned int *const XYZ=0,
29568                     const unsigned char *const color=0) {
29569       return get_select(disp,select_type,XYZ,color).move_to(*this);
29570     }
29571 
29572     //! Simple interface to select a shape from an image.
29573     CImg<T>& select(const char *const title,
29574                     const int select_type=2, unsigned int *const XYZ=0,
29575                     const unsigned char *const color=0) {
29576       return get_select(title,select_type,XYZ,color).move_to(*this);
29577     }
29578 
29579     //! Simple interface to select a shape from an image.
29580     CImg<intT> get_select(CImgDisplay &disp,
29581                           const int select_type=2, unsigned int *const XYZ=0,
29582                           const unsigned char *const color=0) const {
29583       return _get_select(disp,0,select_type,XYZ,color,0,0,0);
29584     }
29585 
29586     //! Simple interface to select a shape from an image.
29587     CImg<intT> get_select(const char *const title,
29588                           const int select_type=2, unsigned int *const XYZ=0,
29589                           const unsigned char *const color=0) const {
29590       CImgDisplay disp;
29591       return _get_select(disp,title,select_type,XYZ,color,0,0,0);
29592     }
29593 
29594     CImg<intT> _get_select(CImgDisplay &disp, const char *const title,
29595                            const int coords_type, unsigned int *const XYZ,
29596                            const unsigned char *const color,
29597                            const int origX, const int origY, const int origZ) const {
29598       if (is_empty())
29599         throw CImgInstanceException(_cimg_instance
29600                                     "select() : Empty instance.",
29601                                     cimg_instance);
29602       if (!disp) {
29603         char ntitle[64] = { 0 }; if (!title) { std::sprintf(ntitle,"CImg<%s>",pixel_type()); }
29604         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:ntitle,1);
29605       }
29606 
29607       const unsigned int
29608         old_normalization = disp.normalization(),
29609         hatch = 0x55555555;
29610 
29611       bool old_is_resized = disp.is_resized();
29612       disp._normalization = 0;
29613       disp.show().set_key(0).set_wheel();
29614 
29615       unsigned char foreground_color[] = { 255,255,105 }, background_color[] = { 0,0,0 };
29616       if (color) std::memcpy(foreground_color,color,sizeof(unsigned char)*cimg::min(3,spectrum()));
29617 
29618       int area = 0, clicked_area = 0, phase = 0,
29619         X0 = (int)((XYZ?XYZ[0]:_width/2)%_width), Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height), Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth),
29620         X1 =-1, Y1 = -1, Z1 = -1,
29621         X = -1, Y = -1, Z = -1,
29622         oX = X, oY = Y, oZ = Z;
29623       unsigned int old_button = 0, key = 0;
29624 
29625       bool shape_selected = false, text_down = false;
29626       CImg<ucharT> visu, visu0;
29627       char text[1024] = { 0 };
29628 
29629       while (!key && !disp.is_closed() && !shape_selected) {
29630 
29631         // Handle mouse motion and selection
29632         oX = X; oY = Y; oZ = Z;
29633         int mx = disp.mouse_x(), my = disp.mouse_y();
29634         const int mX = mx*(_width+(_depth>1?_depth:0))/disp.width(), mY = my*(_height+(_depth>1?_depth:0))/disp.height();
29635 
29636         area = 0;
29637         if (mX<width() && mY<height())  { area = 1; X = mX; Y = mY; Z = phase?Z1:Z0; }
29638         if (mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = phase?Y1:Y0; }
29639         if (mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = phase?X1:X0; }
29640 
29641         switch (key = disp.key()) {
29642 #if cimg_OS!=2
29643         case cimg::keyCTRLRIGHT :
29644 #endif
29645         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
29646         case cimg::keyPAGEUP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
29647         case cimg::keyPAGEDOWN : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
29648         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
29649           disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
29650                                             CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
29651             _is_resized = true;
29652           disp.set_key(key,false); key = 0;
29653         } break;
29654         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
29655           disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
29656           disp.set_key(key,false); key = 0; visu0.assign();
29657         } break;
29658         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
29659           disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
29660           disp.set_key(key,false); key = 0; visu0.assign();
29661         } break;
29662         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
29663           disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
29664           disp.set_key(key,false); key = 0; visu0.assign();
29665         } break;
29666         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
29667           static unsigned int snap_number = 0;
29668           char filename[32] = { 0 };
29669           std::FILE *file;
29670           do {
29671             std::sprintf(filename,"CImg_%.4u.bmp",snap_number++);
29672             if ((file=std::fopen(filename,"r"))!=0) std::fclose(file);
29673           } while (file);
29674           if (visu0) {
29675             visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp);
29676             visu0.save(filename);
29677             visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
29678           }
29679           disp.set_key(key,false); key = 0;
29680         } break;
29681         case cimg::keyO :
29682           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
29683             static unsigned int snap_number = 0;
29684             char filename[32] = { 0 };
29685             std::FILE *file;
29686             do {
29687               std::sprintf(filename,"CImg_%.4u.cimg",snap_number++);
29688               if ((file=std::fopen(filename,"r"))!=0) std::fclose(file);
29689             } while (file);
29690             visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,0.8f,13).display(disp);
29691             save(filename);
29692             visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.8f,13,filename).display(disp);
29693             disp.set_key(key,false); key = 0;
29694           } break;
29695         }
29696 
29697         if (!area) mx = my = X = Y = Z = -1;
29698         else {
29699           if (disp.button()&1 && phase<2) { X1 = X; Y1 = Y; Z1 = Z; }
29700           if (!(disp.button()&1) && phase>=2) {
29701             switch (clicked_area) {
29702             case 1 : Z1 = Z; break;
29703             case 2 : Y1 = Y; break;
29704             case 3 : X1 = X; break;
29705             }
29706           }
29707           if (disp.button()&2) { if (phase) { X1 = X; Y1 = Y; Z1 = Z; } else { X0 = X; Y0 = Y; Z0 = Z; } }
29708           if (disp.button()&4) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu.assign(); }
29709           if (disp.wheel()) {
29710             if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT() &&
29711                 !disp.is_keyALT() && !disp.is_keyALTGR()) {
29712               switch (area) {
29713               case 1 : if (phase) Z = (Z1+=disp.wheel()); else Z = (Z0+=disp.wheel()); break;
29714               case 2 : if (phase) Y = (Y1+=disp.wheel()); else Y = (Y0+=disp.wheel()); break;
29715               case 3 : if (phase) X = (X1+=disp.wheel()); else X = (X0+=disp.wheel()); break;
29716               }
29717               disp.set_wheel();
29718             } else key = ~0U;
29719           }
29720           if ((disp.button()&1)!=old_button) {
29721             switch (phase++) {
29722             case 0 : X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; clicked_area = area; break;
29723             case 1 : X1 = X; Y1 = Y; Z1 = Z; break;
29724             }
29725             old_button = disp.button()&1;
29726           }
29727           if (_depth>1 && (X!=oX || Y!=oY || Z!=oZ)) visu0.assign();
29728         }
29729 
29730         if (phase) {
29731           if (!coords_type) shape_selected = phase?true:false;
29732           else {
29733             if (_depth>1) shape_selected = (phase==3)?true:false;
29734             else shape_selected = (phase==2)?true:false;
29735           }
29736         }
29737 
29738         if (X0<0) X0 = 0; if (X0>=width()) X0 = width() - 1; if (Y0<0) Y0 = 0; if (Y0>=height()) Y0 = height() - 1;
29739         if (Z0<0) Z0 = 0; if (Z0>=depth()) Z0 = depth() - 1;
29740         if (X1<1) X1 = 0; if (X1>=width()) X1 = width() - 1; if (Y1<0) Y1 = 0; if (Y1>=height()) Y1 = height() - 1;
29741         if (Z1<0) Z1 = 0; if (Z1>=depth()) Z1 = depth() - 1;
29742 
29743         // Draw visualization image on the display
29744         if (oX!=X || oY!=Y || oZ!=Z || !visu0) {
29745           if (!visu0) {
29746             CImg<Tuchar> tmp, tmp0;
29747             if (_depth!=1) {
29748               tmp0 = (!phase)?get_projections2d(X0,Y0,Z0):get_projections2d(X1,Y1,Z1);
29749               tmp = tmp0.get_channels(0,cimg::min(2U,_spectrum - 1));
29750             } else tmp = get_channels(0,cimg::min(2U,_spectrum - 1));
29751             switch (old_normalization) {
29752             case 0 : visu0 = tmp; break;
29753             case 3 :
29754               if (cimg::type<T>::is_float()) visu0 = tmp.normalize(0,(T)255);
29755               else {
29756                 const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::max();
29757                 visu0.assign(tmp._width,tmp._height,1,tmp._spectrum);
29758                 unsigned char *ptrd = visu0.end();
29759                 cimg_for(tmp,ptrs,Tuchar) *(--ptrd) = (unsigned char)((*ptrs - m)*255.0f/(M - m));
29760               } break;
29761             default : visu0 = tmp.normalize(0,255);
29762             }
29763             visu0.resize(disp);
29764           }
29765           visu = visu0;
29766           if (!color) {
29767             if (visu.mean()<200) {
29768               foreground_color[0] = foreground_color[1] = foreground_color[2] = 255;
29769               background_color[0] = background_color[1] = background_color[2] = 0;
29770             } else {
29771               foreground_color[0] = foreground_color[1] = foreground_color[2] = 0;
29772               background_color[0] = background_color[1] = background_color[2] = 255;
29773             }
29774           }
29775 
29776           const int d = (_depth>1)?_depth:0;
29777           if (phase) switch (coords_type) {
29778           case 1 : {
29779             const int
29780               x0 = (int)((X0+0.5f)*disp.width()/(_width+d)),
29781               y0 = (int)((Y0+0.5f)*disp.height()/(_height+d)),
29782               x1 = (int)((X1+0.5f)*disp.width()/(_width+d)),
29783               y1 = (int)((Y1+0.5f)*disp.height()/(_height+d));
29784             visu.draw_arrow(x0,y0,x1,y1,foreground_color,0.6f,30,5,hatch);
29785             if (d) {
29786               const int
29787                 zx0 = (int)((_width+Z0+0.5f)*disp.width()/(_width+d)),
29788                 zx1 = (int)((_width+Z1+0.5f)*disp.width()/(_width+d)),
29789                 zy0 = (int)((_height+Z0+0.5f)*disp.height()/(_height+d)),
29790                 zy1 = (int)((_height+Z1+0.5f)*disp.height()/(_height+d));
29791               visu.draw_arrow(zx0,y0,zx1,y1,foreground_color,0.6f,30,5,hatch).
29792                 draw_arrow(x0,zy0,x1,zy1,foreground_color,0.6f,30,5,hatch);
29793             }
29794           } break;
29795           case 2 : {
29796             const int
29797               x0 = (X0<X1?X0:X1)*disp.width()/(_width+d),
29798               y0 = (Y0<Y1?Y0:Y1)*disp.height()/(_height+d),
29799               x1 = ((X0<X1?X1:X0)+1)*disp.width()/(_width+d)-1,
29800               y1 = ((Y0<Y1?Y1:Y0)+1)*disp.height()/(_height+d)-1;
29801             visu.draw_rectangle(x0,y0,x1,y1,foreground_color,0.2f).draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,hatch);
29802             if (d) {
29803               const int
29804                 zx0 = (int)((_width+(Z0<Z1?Z0:Z1))*disp.width()/(_width+d)),
29805                 zy0 = (int)((_height+(Z0<Z1?Z0:Z1))*disp.height()/(_height+d)),
29806                 zx1 = (int)((_width+(Z0<Z1?Z1:Z0)+1)*disp.width()/(_width+d))-1,
29807                 zy1 = (int)((_height+(Z0<Z1?Z1:Z0)+1)*disp.height()/(_height+d))-1;
29808               visu.draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.2f).draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.6f,hatch);
29809               visu.draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.2f).draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.6f,hatch);
29810             }
29811           } break;
29812           case 3 : {
29813             const int
29814               x0 = X0*disp.width()/(_width+d),
29815               y0 = Y0*disp.height()/(_height+d),
29816               x1 = X1*disp.width()/(_width+d)-1,
29817               y1 = Y1*disp.height()/(_height+d)-1;
29818             visu.draw_ellipse(x0,y0,(float)(x1-x0),(float)(y1-y0),0,foreground_color,0.2f).
29819               draw_ellipse(x0,y0,(float)(x1-x0),(float)(y1-y0),0,foreground_color,0.6f,hatch);
29820             if (d) {
29821               const int
29822                 zx0 = (int)((_width+Z0)*disp.width()/(_width+d)),
29823                 zy0 = (int)((_height+Z0)*disp.height()/(_height+d)),
29824                 zx1 = (int)((_width+Z1+1)*disp.width()/(_width+d))-1,
29825                 zy1 = (int)((_height+Z1+1)*disp.height()/(_height+d))-1;
29826               visu.draw_ellipse(zx0,y0,(float)(zx1-zx0),(float)(y1-y0),0,foreground_color,0.2f).
29827                 draw_ellipse(zx0,y0,(float)(zx1-zx0),(float)(y1-y0),0,foreground_color,0.6f,hatch).
29828                 draw_ellipse(x0,zy0,(float)(x1-x0),(float)(zy1-zy0),0,foreground_color,0.2f).
29829                 draw_ellipse(x0,zy0,(float)(x1-x0),(float)(zy1-zy0),0,foreground_color,0.6f,hatch);
29830             }
29831           } break;
29832           } else {
29833             const int
29834               x0 = X*disp.width()/(_width+d),
29835               y0 = Y*disp.height()/(_height+d),
29836               x1 = (X+1)*disp.width()/(_width+d)-1,
29837               y1 = (Y+1)*disp.height()/(_height+d)-1;
29838             if (x1-x0>=4 && y1-y0>=4) visu.draw_rectangle(x0,y0,x1,y1,foreground_color,0.4f,~0U);
29839           }
29840 
29841           if (my>=0 && my<13) text_down = true; else if (my>=visu.height()-13) text_down = false;
29842           if (!coords_type || !phase) {
29843             if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
29844               if (_depth>1) std::sprintf(text," Point (%d,%d,%d) = [ ",origX+X,origY+Y,origZ+Z);
29845               else std::sprintf(text," Point (%d,%d) = [ ",origX+X,origY+Y);
29846               char *ctext = text + std::strlen(text), *const ltext = text + 512;
29847               for (unsigned int c = 0; c<_spectrum && ctext<ltext; ++c) {
29848                 std::sprintf(ctext,cimg::type<T>::format(),cimg::type<T>::format((*this)(X,Y,Z,c)));
29849                 ctext = text + std::strlen(text);
29850                 *(ctext++) = ' '; *ctext = 0;
29851               }
29852               std::sprintf(text + std::strlen(text),"] ");
29853             }
29854           } else switch (coords_type) {
29855           case 1 : {
29856             const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), norm = std::sqrt(dX*dX+dY*dY+dZ*dZ);
29857             if (_depth>1) std::sprintf(text," Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g ",
29858                                       origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,norm);
29859             else std::sprintf(text," Vect (%d,%d)-(%d,%d), Norm = %g ",
29860                               origX+X0,origY+Y0,origX+X1,origY+Y1,norm);
29861           } break;
29862           case 2 :
29863             if (_depth>1) std::sprintf(text," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ",
29864                                       origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origZ+(Z0<Z1?Z0:Z1),
29865                                       origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),origZ+(Z0<Z1?Z1:Z0),
29866                                       1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
29867             else  std::sprintf(text," Box (%d,%d)-(%d,%d), Size = (%d,%d) ",
29868                                origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),
29869                                1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
29870             break;
29871           default :
29872             if (_depth>1) std::sprintf(text," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ",
29873                                       origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,
29874                                       1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
29875             else  std::sprintf(text," Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d) ",
29876                                origX+X0,origY+Y0,origX+X1,origY+Y1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
29877 
29878           }
29879           if (phase || (mx>=0 && my>=0)) visu.draw_text(0,text_down?visu.height()-13:0,text,foreground_color,background_color,0.7f,13);
29880           disp.display(visu).wait(25);
29881         } else if (!shape_selected) disp.wait();
29882         if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
29883       }
29884 
29885       // Return result
29886       CImg<intT> res(1,6,1,1,-1);
29887       if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
29888       if (shape_selected) {
29889         if (coords_type==2) {
29890           if (X0>X1) cimg::swap(X0,X1);
29891           if (Y0>Y1) cimg::swap(Y0,Y1);
29892           if (Z0>Z1) cimg::swap(Z0,Z1);
29893         }
29894         if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
29895         switch (coords_type) {
29896         case 1 : case 2 : res[3] = X1; res[4] = Y1; res[5] = Z1;
29897         default : res[0] = X0; res[1] = Y0; res[2] = Z0;
29898         }
29899       }
29900       disp.set_button();
29901       disp._normalization = old_normalization;
29902       disp._is_resized = old_is_resized;
29903       if (key!=~0U) disp.set_key(key);
29904       return res;
29905     }
29906 
29907     //! Select sub-graph in a graph.
29908     CImg<intT> get_select_graph(CImgDisplay &disp,
29909                                 const unsigned int plot_type=1, const unsigned int vertex_type=1,
29910                                 const char *const labelx=0, const double xmin=0, const double xmax=0,
29911                                 const char *const labely=0, const double ymin=0, const double ymax=0) const {
29912       if (is_empty())
29913         throw CImgInstanceException(_cimg_instance
29914                                     "display_graph() : Empty instance.",
29915                                     cimg_instance);
29916 
29917       const unsigned int siz = _width*_height*_depth, onormalization = disp.normalization();
29918       if (!disp) { char ntitle[64] = { 0 }; std::sprintf(ntitle,"CImg<%s>",pixel_type()); disp.assign(640,480,ntitle,0); }
29919       (disp.show().set_button().set_wheel())._normalization = 0;
29920       double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
29921       if (nymin==nymax) nymin = (Tfloat)min_max(nymax);
29922       if (nymin==nymax) { --nymin; ++nymax; }
29923       if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; }
29924 
29925       const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
29926       const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
29927       static unsigned int odimv = 0;
29928       static CImg<ucharT> palette;
29929       if (odimv!=_spectrum) {
29930         odimv = _spectrum;
29931         palette = CImg<ucharT>(3,_spectrum,1,1,120).noise(70,1);
29932         if (_spectrum==1) { palette[0] = palette[1] = 120; palette[2] = 200; }
29933         else {
29934           palette(0,0) = 220; palette(1,0) = 10; palette(2,0) = 10;
29935           if (_spectrum>1) { palette(0,1) = 10; palette(1,1) = 220; palette(2,1) = 10; }
29936           if (_spectrum>2) { palette(0,2) = 10; palette(1,2) = 10; palette(2,2) = 220; }
29937         }
29938       }
29939 
29940       CImg<ucharT> visu0, visu, graph, text, axes;
29941       const unsigned int whd = _width*_height*_depth;
29942       int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
29943       char message[1024] = { 0 };
29944       unsigned int okey = 0, obutton = 0;
29945       CImg_3x3(I,unsigned char);
29946 
29947       for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
29948         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
29949         const unsigned int key = disp.key(), button = disp.button();
29950 
29951         // Generate graph representation.
29952         if (!visu0) {
29953           visu0.assign(disp.width(),disp.height(),1,3,220);
29954           const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
29955           if (gdimx>0 && gdimy>0) {
29956             graph.assign(gdimx,gdimy,1,3,255);
29957             graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
29958             cimg_forC(*this,c) graph.draw_graph(get_shared_channel(c),&palette(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
29959                                                 plot_type,vertex_type,nymax,nymin,false);
29960 
29961             axes.assign(gdimx,gdimy,1,1,0);
29962             const float
29963               dx = (float)cimg::abs(nxmax-nxmin), dy = (float)cimg::abs(nymax-nymin),
29964               px = (float)std::pow(10.0,(int)std::log10(dx)-2.0),
29965               py = (float)std::pow(10.0,(int)std::log10(dy)-2.0);
29966             const CImg<Tdouble>
29967               seqx = CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,nxmax).round(px),
29968               seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin).round(py);
29969             axes.draw_axes(seqx,seqy,white);
29970             if (nymin>0) axes.draw_axis(seqx,gdimy-1,gray);
29971             if (nymax<0) axes.draw_axis(seqx,0,gray);
29972             if (nxmin>0) axes.draw_axis(0,seqy,gray);
29973             if (nxmax<0) axes.draw_axis(gdimx-1,seqy,gray);
29974 
29975             cimg_for3x3(axes,x,y,0,0,I,unsigned char)
29976               if (Icc) {
29977                 if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
29978                 else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
29979               }
29980               else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) cimg_forC(graph,c) graph(x,y,c) = (graph(x,y,c)+255)/2;
29981 
29982             visu0.draw_image(16,16,graph);
29983             visu0.draw_line(15,15,16+gdimx,15,gray2).draw_line(16+gdimx,15,16+gdimx,16+gdimy,gray2).
29984               draw_line(16+gdimx,16+gdimy,15,16+gdimy,white).draw_line(15,16+gdimy,15,15,white);
29985           } else graph.assign();
29986           text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13);
29987           visu0.draw_image((visu0.width()-text.width())/2,visu0.height()-14,~text);
29988           text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90);
29989           visu0.draw_image(2,(visu0.height()-text.height())/2,~text);
29990           visu.assign();
29991         }
29992 
29993         // Generate and display current view.
29994         if (!visu) {
29995           visu.assign(visu0);
29996           if (graph && x0>=0 && x1>=0) {
29997             const int
29998               nx0 = x0<=x1?x0:x1,
29999               nx1 = x0<=x1?x1:x0,
30000               ny0 = y0<=y1?y0:y1,
30001               ny1 = y0<=y1?y1:y0,
30002               sx0 = 16 + nx0*(visu.width()-32)/whd,
30003               sx1 = 15 + (nx1+1)*(visu.width()-32)/whd,
30004               sy0 = 16 + ny0,
30005               sy1 = 16 + ny1;
30006 
30007             if (y0>=0 && y1>=0)
30008               visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
30009             else visu.draw_rectangle(sx0,0,sx1,visu.height()-17,gray,0.5f).
30010                    draw_line(sx0,16,sx0,visu.height()-17,black,0.5f,0xCCCCCCCCU).
30011                    draw_line(sx1,16,sx1,visu.height()-17,black,0.5f,0xCCCCCCCCU);
30012           }
30013           if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width()-16 && mouse_y<visu.height()-16) {
30014             if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height()-17,black,0.5f,0x55555555U);
30015             const unsigned x = (mouse_x-16)*whd/(disp.width()-32);
30016             const double cx = nxmin + x*(nxmax-nxmin)/whd;
30017             if (_spectrum>=7)
30018               std::sprintf(message,"Value[%g] = ( %g %g %g ... %g %g %g )",cx,
30019                            (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
30020                            (double)(*this)(x,0,0,_spectrum-4),(double)(*this)(x,0,0,_spectrum-3),(double)(*this)(x,0,0,_spectrum-1));
30021             else {
30022               std::sprintf(message,"Value[%g] = ( ",cx);
30023               cimg_forC(*this,c) std::sprintf(message+std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
30024               std::sprintf(message+std::strlen(message),")");
30025             }
30026             if (x0>=0 && x1>=0) {
30027               const int
30028                  nx0 = x0<=x1?x0:x1,
30029                  nx1 = x0<=x1?x1:x0,
30030                  ny0 = y0<=y1?y0:y1,
30031                  ny1 = y0<=y1?y1:y0;
30032               const double
30033                  cx0 = nxmin + nx0*(nxmax-nxmin)/(visu.width()-32),
30034                  cx1 = nxmin + nx1*(nxmax-nxmin)/(visu.width()-32),
30035                  cy0 = nymax - ny0*(nymax-nymin)/(visu.height()-32),
30036                  cy1 = nymax - ny1*(nymax-nymin)/(visu.height()-32);
30037               if (y0>=0 && y1>=0)
30038                 std::sprintf(message+std::strlen(message)," - Range ( %g, %g ) - ( %g, %g )",cx0,cy0,cx1,cy1);
30039               else
30040                 std::sprintf(message+std::strlen(message)," - Range [ %g - %g ]",cx0,cx1);
30041             }
30042             text.assign().draw_text(0,0,message,white,ngray,1,13);
30043             visu.draw_image((visu.width()-text.width())/2,1,~text);
30044           }
30045           visu.display(disp);
30046         }
30047 
30048         // Test keys.
30049         switch (okey = key) {
30050 #if cimg_OS!=2
30051         case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
30052 #endif
30053         case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
30054         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
30055           disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
30056                                             CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
30057             _is_resized = true;
30058           disp.set_key(key,false); okey = 0;
30059         } break;
30060         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
30061           disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
30062           disp.set_key(key,false); okey = 0;
30063         } break;
30064         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
30065           disp.set_fullscreen(false).resize(cimg_fitscreen(640,480,1),false)._is_resized = true;
30066           disp.set_key(key,false); okey = 0;
30067         } break;
30068         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
30069           disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
30070           disp.set_key(key,false); okey = 0;
30071         } break;
30072         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
30073           static unsigned int snap_number = 0;
30074           if (visu || visu0) {
30075             CImg<ucharT> &screen = visu?visu:visu0;
30076             char filename[32] = { 0 };
30077             std::FILE *file;
30078             do {
30079               std::sprintf(filename,"CImg_%.4u.bmp",snap_number++);
30080               if ((file=std::fopen(filename,"r"))!=0) std::fclose(file);
30081             } while (file);
30082             (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp);
30083             screen.save(filename);
30084             screen.draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename).display(disp);
30085           }
30086           disp.set_key(key,false); okey = 0;
30087         } break;
30088         }
30089 
30090         // Handle mouse motion and mouse buttons
30091         if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
30092           visu.assign();
30093           if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
30094             const int
30095               mx = (mouse_x-16)*(int)whd/(disp.width()-32),
30096               cx = mx<0?0:(mx>=(int)whd?whd-1:mx),
30097               my = mouse_y-16,
30098               cy = my<=0?0:(my>=(disp.height()-32)?(disp.height()-32):my);
30099             if (button&1) {
30100               if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
30101             }
30102             else if (button&2) {
30103               if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
30104             }
30105             else if (obutton) { x1 = cx; y1 = y1>=0?cy:-1; selected = true; }
30106           } else if (!button && obutton) selected = true;
30107           obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
30108         }
30109         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
30110         if (visu && visu0) disp.wait();
30111       }
30112       disp._normalization = onormalization;
30113       if (x1<x0) cimg::swap(x0,x1);
30114       if (y1<y0) cimg::swap(y0,y1);
30115       disp.set_key(okey);
30116       return CImg<intT>(4,1,1,1,x0,y0,x1,y1);
30117     }
30118 
30119     //! Load an image from a file.
30120     /**
30121        \param filename is the name of the image file to load.
30122        \note The extension of \c filename defines the file format. If no filename
30123        extension is provided, CImg<T>::get_load() will try to load a .cimg file.
30124     **/
30125     CImg<T>& load(const char *const filename) {
30126       if (!filename)
30127         throw CImgArgumentException(_cimg_instance
30128                                     "load() : Specified filename is (null).",
30129                                     cimg_instance);
30130 
30131       const char *const ext = cimg::split_filename(filename);
30132       const unsigned int omode = cimg::exception_mode();
30133       cimg::exception_mode() = 0;
30134       try {
30135 #ifdef cimg_load_plugin
30136         cimg_load_plugin(filename);
30137 #endif
30138 #ifdef cimg_load_plugin1
30139         cimg_load_plugin1(filename);
30140 #endif
30141 #ifdef cimg_load_plugin2
30142         cimg_load_plugin2(filename);
30143 #endif
30144 #ifdef cimg_load_plugin3
30145         cimg_load_plugin3(filename);
30146 #endif
30147 #ifdef cimg_load_plugin4
30148         cimg_load_plugin4(filename);
30149 #endif
30150 #ifdef cimg_load_plugin5
30151         cimg_load_plugin5(filename);
30152 #endif
30153 #ifdef cimg_load_plugin6
30154         cimg_load_plugin6(filename);
30155 #endif
30156 #ifdef cimg_load_plugin7
30157         cimg_load_plugin7(filename);
30158 #endif
30159 #ifdef cimg_load_plugin8
30160         cimg_load_plugin8(filename);
30161 #endif
30162         // ASCII formats
30163         if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
30164         else if (!cimg::strcasecmp(ext,"dlm") ||
30165                  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
30166 
30167         // 2d binary formats
30168         else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
30169         else if (!cimg::strcasecmp(ext,"jpg") ||
30170                  !cimg::strcasecmp(ext,"jpeg") ||
30171                  !cimg::strcasecmp(ext,"jpe") ||
30172                  !cimg::strcasecmp(ext,"jfif") ||
30173                  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
30174         else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
30175         else if (!cimg::strcasecmp(ext,"ppm") ||
30176                  !cimg::strcasecmp(ext,"pgm") ||
30177                  !cimg::strcasecmp(ext,"pnm")) load_pnm(filename);
30178         else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
30179         else if (!cimg::strcasecmp(ext,"tif") ||
30180                  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
30181         else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
30182         else if (!cimg::strcasecmp(ext,"cr2") ||
30183                  !cimg::strcasecmp(ext,"crw") ||
30184                  !cimg::strcasecmp(ext,"dcr") ||
30185                  !cimg::strcasecmp(ext,"mrw") ||
30186                  !cimg::strcasecmp(ext,"nef") ||
30187                  !cimg::strcasecmp(ext,"orf") ||
30188                  !cimg::strcasecmp(ext,"pix") ||
30189                  !cimg::strcasecmp(ext,"ptx") ||
30190                  !cimg::strcasecmp(ext,"raf") ||
30191                  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
30192 
30193         // 3d binary formats
30194         else if (!cimg::strcasecmp(ext,"dcm") ||
30195                  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
30196         else if (!cimg::strcasecmp(ext,"hdr") ||
30197                  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
30198         else if (!cimg::strcasecmp(ext,"par") ||
30199                  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
30200         else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
30201         else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
30202         else if (!cimg::strcasecmp(ext,"cimg") ||
30203                  !cimg::strcasecmp(ext,"cimgz") ||
30204                  !*ext)  return load_cimg(filename);
30205 
30206         // Archive files
30207         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
30208 
30209         // Image sequences
30210         else if (!cimg::strcasecmp(ext,"avi") ||
30211                  !cimg::strcasecmp(ext,"mov") ||
30212                  !cimg::strcasecmp(ext,"asf") ||
30213                  !cimg::strcasecmp(ext,"divx") ||
30214                  !cimg::strcasecmp(ext,"flv") ||
30215                  !cimg::strcasecmp(ext,"mpg") ||
30216                  !cimg::strcasecmp(ext,"m1v") ||
30217                  !cimg::strcasecmp(ext,"m2v") ||
30218                  !cimg::strcasecmp(ext,"m4v") ||
30219                  !cimg::strcasecmp(ext,"mjp") ||
30220                  !cimg::strcasecmp(ext,"mkv") ||
30221                  !cimg::strcasecmp(ext,"mpe") ||
30222                  !cimg::strcasecmp(ext,"movie") ||
30223                  !cimg::strcasecmp(ext,"ogm") ||
30224                  !cimg::strcasecmp(ext,"qt") ||
30225                  !cimg::strcasecmp(ext,"rm") ||
30226                  !cimg::strcasecmp(ext,"vob") ||
30227                  !cimg::strcasecmp(ext,"wmv") ||
30228                  !cimg::strcasecmp(ext,"xvid") ||
30229                  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
30230         else throw CImgIOException("CImg<%s>::load()",
30231                                    pixel_type());
30232       } catch (CImgException& e) {
30233         if (!cimg::strncasecmp(e.what(),"cimg::fopen()",13)) {
30234           cimg::exception_mode() = omode;
30235           throw CImgIOException(_cimg_instance
30236                                 "load() : Failed to open file '%s'.",
30237                                 cimg_instance,
30238                                 filename);
30239         } else try {
30240           const char *const f_type = cimg::file_type(0,filename);
30241           if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
30242           else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
30243           else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
30244           else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
30245           else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
30246           else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
30247           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
30248           else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
30249           else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
30250           else throw CImgIOException("CImg<%s>::load()",
30251                                      pixel_type());
30252         } catch (CImgException&) {
30253           try {
30254             load_other(filename);
30255           } catch (CImgException&) {
30256             throw CImgIOException(_cimg_instance
30257                                   "load() : Failed to recognize format of file '%s'.",
30258                                   cimg_instance,
30259                                   filename);
30260           }
30261         }
30262       }
30263       cimg::exception_mode() = omode;
30264       return *this;
30265     }
30266 
30267     static CImg<T> get_load(const char *const filename) {
30268       return CImg<T>().load(filename);
30269     }
30270 
30271     //! Load an image from an ASCII file.
30272     CImg<T>& load_ascii(const char *const filename) {
30273       return _load_ascii(0,filename);
30274     }
30275 
30276     static CImg<T> get_load_ascii(const char *const filename) {
30277       return CImg<T>().load_ascii(filename);
30278     }
30279 
30280     //! Load an image from an ASCII file.
30281     CImg<T>& load_ascii(std::FILE *const file) {
30282       return _load_ascii(file,0);
30283     }
30284 
30285     static CImg<T> get_load_ascii(std::FILE *const file) {
30286       return CImg<T>().load_ascii(file);
30287     }
30288 
30289     CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
30290       if (!file && !filename)
30291         throw CImgArgumentException(_cimg_instance
30292                                     "load_ascii() : Specified filename is (null).",
30293                                     cimg_instance);
30294 
30295       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
30296       char line[256] = { 0 };
30297       int err = std::fscanf(nfile,"%255[^\n]",line);
30298       unsigned int off, dx = 0, dy = 1, dz = 1, dc = 1;
30299       std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
30300       err = std::fscanf(nfile,"%*[^0-9.eE+-]");
30301       if (!dx || !dy || !dz || !dc) {
30302         if (!file) cimg::fclose(nfile);
30303         throw CImgIOException(_cimg_instance
30304                               "load_ascii() : Invalid ASCII header in file '%s', image dimensions are set to (%u,%u,%u,%u).",
30305                               cimg_instance,
30306                               filename?filename:"(FILE*)",dx,dy,dz,dc);
30307       }
30308       assign(dx,dy,dz,dc);
30309       const unsigned int siz = size();
30310       double val;
30311       T *ptr = _data;
30312       for (err = 1, off = 0; off<siz && err==1; ++off) {
30313         err = std::fscanf(nfile,"%lf%*[^0-9.eE+-]",&val);
30314         *(ptr++) = (T)val;
30315       }
30316       if (err!=1)
30317         cimg::warn(_cimg_instance
30318                    "load_ascii() : Only %u/%u values read from file '%s'.",
30319                    cimg_instance,
30320                    off-1,siz,filename?filename:"(FILE*)");
30321 
30322       if (!file) cimg::fclose(nfile);
30323       return *this;
30324     }
30325 
30326     //! Load an image from a DLM file.
30327     CImg<T>& load_dlm(const char *const filename) {
30328       return _load_dlm(0,filename);
30329     }
30330 
30331     static CImg<T> get_load_dlm(const char *const filename) {
30332       return CImg<T>().load_dlm(filename);
30333     }
30334 
30335     //! Load an image from a DLM file.
30336     CImg<T>& load_dlm(std::FILE *const file) {
30337       return _load_dlm(file,0);
30338     }
30339 
30340     static CImg<T> get_load_dlm(std::FILE *const file) {
30341       return CImg<T>().load_dlm(file);
30342     }
30343 
30344     CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
30345       if (!file && !filename)
30346         throw CImgArgumentException(_cimg_instance
30347                                     "load_dlm() : Specified filename is (null).",
30348                                     cimg_instance);
30349 
30350       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
30351       char delimiter[256] = { 0 }, tmp[256] = { 0 };
30352       unsigned int cdx = 0, dx = 0, dy = 0;
30353       int err = 0;
30354       double val;
30355       assign(256,256);
30356       while ((err = std::fscanf(nfile,"%lf%255[^0-9.+-]",&val,delimiter))>0) {
30357         if (err>0) (*this)(cdx++,dy) = (T)val;
30358         if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
30359         char c = 0;
30360         if (!std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') {
30361           dx = cimg::max(cdx,dx);
30362           if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
30363           cdx = 0;
30364         }
30365       }
30366       if (cdx && err==1) { dx = cdx; ++dy; }
30367       if (!dx || !dy) {
30368         if (!file) cimg::fclose(nfile);
30369         throw CImgIOException(_cimg_instance
30370                               "load_dlm() : Invalid DLM file '%s'.",
30371                               cimg_instance,
30372                               filename?filename:"(FILE*)");
30373       }
30374       resize(dx,dy,1,1,0);
30375       if (!file) cimg::fclose(nfile);
30376       return *this;
30377     }
30378 
30379     //! Load an image from a BMP file.
30380     CImg<T>& load_bmp(const char *const filename) {
30381       return _load_bmp(0,filename);
30382     }
30383 
30384     static CImg<T> get_load_bmp(const char *const filename) {
30385       return CImg<T>().load_bmp(filename);
30386     }
30387 
30388     //! Load an image from a BMP file.
30389     CImg<T>& load_bmp(std::FILE *const file) {
30390       return _load_bmp(file,0);
30391     }
30392 
30393     static CImg<T> get_load_bmp(std::FILE *const file) {
30394       return CImg<T>().load_bmp(file);
30395     }
30396 
30397     CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
30398       if (!file && !filename)
30399         throw CImgArgumentException(_cimg_instance
30400                                     "load_bmp() : Specified filename is (null).",
30401                                     cimg_instance);
30402 
30403       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
30404       unsigned char header[64] = { 0 };
30405       cimg::fread(header,54,nfile);
30406       if (header[0]!='B' || header[1]!='M') {
30407         if (!file) cimg::fclose(nfile);
30408         throw CImgIOException(_cimg_instance
30409                               "load_bmp() : Invalid BMP file '%s'.",
30410                               cimg_instance,
30411                               filename?filename:"(FILE*)");
30412       }
30413 
30414       // Read header and pixel buffer
30415       int
30416         file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
30417         offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
30418         dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
30419         dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
30420         compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
30421         nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
30422         bpp = header[0x1C] + (header[0x1D]<<8);
30423       const int
30424         cimg_iobuffer = 12*1024*1024,
30425         dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)),
30426         align_bytes = (4-dx_bytes%4)%4,
30427         buf_size = cimg::min(cimg::abs(dy)*(dx_bytes+align_bytes),file_size-offset);
30428 
30429       CImg<intT> palette;
30430       if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
30431       if (nb_colors) { palette.assign(nb_colors); cimg::fread(palette._data,nb_colors,nfile); }
30432       const int xoffset = offset - 54 - 4*nb_colors;
30433       if (xoffset>0) std::fseek(nfile,xoffset,SEEK_CUR);
30434 
30435       CImg<ucharT> buffer;
30436       if (buf_size<cimg_iobuffer) { buffer.assign(buf_size); cimg::fread(buffer._data,buf_size,nfile); }
30437       else buffer.assign(dx_bytes + align_bytes);
30438       unsigned char *ptrs = buffer;
30439 
30440       // Decompress buffer (if necessary)
30441       if (compression) {
30442         if (file)
30443           throw CImgIOException(_cimg_instance
30444                                 "load_bmp() : Unable to load compressed data from '(*FILE)' inputs.",
30445                                 cimg_instance);
30446         else return load_other(filename);
30447       }
30448 
30449       // Read pixel data
30450       assign(dx,cimg::abs(dy),1,3);
30451       switch (bpp) {
30452       case 1 : { // Monochrome
30453         for (int y = height()-1; y>=0; --y) {
30454           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
30455           unsigned char mask = 0x80, val = 0;
30456           cimg_forX(*this,x) {
30457             if (mask==0x80) val = *(ptrs++);
30458             const unsigned char *col = (unsigned char*)(palette._data + (val&mask?1:0));
30459             (*this)(x,y,2) = (T)*(col++);
30460             (*this)(x,y,1) = (T)*(col++);
30461             (*this)(x,y,0) = (T)*(col++);
30462             mask = cimg::ror(mask);
30463           }
30464           ptrs+=align_bytes;
30465         }
30466       } break;
30467       case 4 : { // 16 colors
30468         for (int y = height()-1; y>=0; --y) {
30469           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
30470           unsigned char mask = 0xF0, val = 0;
30471           cimg_forX(*this,x) {
30472             if (mask==0xF0) val = *(ptrs++);
30473             const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
30474             const unsigned char *col = (unsigned char*)(palette._data + color);
30475             (*this)(x,y,2) = (T)*(col++);
30476             (*this)(x,y,1) = (T)*(col++);
30477             (*this)(x,y,0) = (T)*(col++);
30478             mask = cimg::ror(mask,4);
30479           }
30480           ptrs+=align_bytes;
30481         }
30482       } break;
30483       case 8 : { //  256 colors
30484         for (int y = height()-1; y>=0; --y) {
30485           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
30486           cimg_forX(*this,x) {
30487             const unsigned char *col = (unsigned char*)(palette._data + *(ptrs++));
30488             (*this)(x,y,2) = (T)*(col++);
30489             (*this)(x,y,1) = (T)*(col++);
30490             (*this)(x,y,0) = (T)*(col++);
30491           }
30492           ptrs+=align_bytes;
30493         }
30494       } break;
30495       case 16 : { // 16 bits colors
30496         for (int y = height()-1; y>=0; --y) {
30497           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
30498           cimg_forX(*this,x) {
30499             const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
30500             const unsigned short col = (unsigned short)(c1|(c2<<8));
30501             (*this)(x,y,2) = (T)(col&0x1F);
30502             (*this)(x,y,1) = (T)((col>>5)&0x1F);
30503             (*this)(x,y,0) = (T)((col>>10)&0x1F);
30504           }
30505           ptrs+=align_bytes;
30506         }
30507       } break;
30508       case 24 : { // 24 bits colors
30509         for (int y = height()-1; y>=0; --y) {
30510           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
30511           cimg_forX(*this,x) {
30512             (*this)(x,y,2) = (T)*(ptrs++);
30513             (*this)(x,y,1) = (T)*(ptrs++);
30514             (*this)(x,y,0) = (T)*(ptrs++);
30515           }
30516           ptrs+=align_bytes;
30517         }
30518       } break;
30519       case 32 : { // 32 bits colors
30520         for (int y = height()-1; y>=0; --y) {
30521           if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
30522           cimg_forX(*this,x) {
30523             (*this)(x,y,2) = (T)*(ptrs++);
30524             (*this)(x,y,1) = (T)*(ptrs++);
30525             (*this)(x,y,0) = (T)*(ptrs++);
30526             ++ptrs;
30527           }
30528           ptrs+=align_bytes;
30529         }
30530       } break;
30531       }
30532       if (dy<0) mirror('y');
30533       if (!file) cimg::fclose(nfile);
30534       return *this;
30535     }
30536 
30537     //! Load an image from a JPEG file.
30538     CImg<T>& load_jpeg(const char *const filename) {
30539       return _load_jpeg(0,filename);
30540     }
30541 
30542     static CImg<T> get_load_jpeg(const char *const filename) {
30543       return CImg<T>().load_jpeg(filename);
30544     }
30545 
30546     //! Load an image from a JPEG file.
30547     CImg<T>& load_jpeg(std::FILE *const file) {
30548       return _load_jpeg(file,0);
30549     }
30550 
30551     static CImg<T> get_load_jpeg(std::FILE *const file) {
30552       return CImg<T>().load_jpeg(file);
30553     }
30554 
30555     // Custom error handler for libjpeg.
30556 #ifdef cimg_use_jpeg
30557     METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
30558       char message[JMSG_LENGTH_MAX];
30559       (*cinfo->err->format_message)(cinfo,message);  // Create the message.
30560       jpeg_destroy(cinfo);                           // Clean memory and temp files.
30561       throw CImgIOException("CImg<T>::load_jpeg() : Error message returned by libjpeg : %s.",message);
30562     }
30563 #endif
30564 
30565     CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
30566       if (!file && !filename)
30567         throw CImgArgumentException(_cimg_instance
30568                                     "load_jpeg() : Specified filename is (null).",
30569                                     cimg_instance);
30570 
30571 #ifndef cimg_use_jpeg
30572       if (file)
30573         throw CImgIOException(_cimg_instance
30574                               "load_jpeg() : Unable to load data from '(FILE*)' unless libjpeg is enabled.",
30575                               cimg_instance);
30576       else return load_other(filename);
30577 #else
30578 
30579       struct jpeg_decompress_struct cinfo;
30580       struct jpeg_error_mgr jerr;
30581       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
30582 
30583       cinfo.err = jpeg_std_error(&jerr);
30584       jerr.error_exit = _cimg_jpeg_error_exit;
30585       jpeg_create_decompress(&cinfo);
30586       jpeg_stdio_src(&cinfo,nfile);
30587       jpeg_read_header(&cinfo,TRUE);
30588       jpeg_start_decompress(&cinfo);
30589 
30590       if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
30591         if (!file) return load_other(filename);
30592         else
30593           throw CImgIOException(_cimg_instance
30594                                 "load_jpeg() : Failed to load JPEG data from file '%s'.",
30595                                 cimg_instance,filename?filename:"(FILE*)");
30596       }
30597       CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
30598       JSAMPROW row_pointer[1];
30599       assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components);
30600       T *ptr_r = _data, *ptr_g = _data + _width*_height, *ptr_b = _data + 2*_width*_height, *ptr_a = _data + 3*_width*_height;
30601       while (cinfo.output_scanline<cinfo.output_height) {
30602         row_pointer[0] = buffer._data;
30603         if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
30604           cimg::warn(_cimg_instance
30605                      "load_jpeg() : Incomplete data in file '%s'.",
30606                      cimg_instance,filename?filename:"(FILE*)");
30607           break;
30608         }
30609         const unsigned char *ptrs = buffer._data;
30610         switch (_spectrum) {
30611         case 1 : {
30612           cimg_forX(*this,x) *(ptr_g++) = (T)*(ptrs++);
30613         } break;
30614         case 3 : {
30615           cimg_forX(*this,x) {
30616             *(ptr_r++) = (T)*(ptrs++);
30617             *(ptr_g++) = (T)*(ptrs++);
30618             *(ptr_b++) = (T)*(ptrs++);
30619           }
30620         } break;
30621         case 4 : {
30622           cimg_forX(*this,x) {
30623             *(ptr_r++) = (T)*(ptrs++);
30624             *(ptr_g++) = (T)*(ptrs++);
30625             *(ptr_b++) = (T)*(ptrs++);
30626             *(ptr_a++) = (T)*(ptrs++);
30627           }
30628         } break;
30629         }
30630       }
30631       jpeg_finish_decompress(&cinfo);
30632       jpeg_destroy_decompress(&cinfo);
30633       if (!file) cimg::fclose(nfile);
30634       return *this;
30635 #endif
30636     }
30637 
30638     //! Load an image from a file, using Magick++ library.
30639     // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
30640     //   This is experimental code, not much tested, use with care.
30641     CImg<T>& load_magick(const char *const filename) {
30642       if (!filename)
30643         throw CImgArgumentException(_cimg_instance
30644                                     "load_magick() : Specified filename is (null).",
30645                                     cimg_instance);
30646 
30647 #ifdef cimg_use_magick
30648       Magick::Image image(filename);
30649       const unsigned int W = image.size().width(), H = image.size().height();
30650       switch (image.type()) {
30651       case Magick::PaletteMatteType :
30652       case Magick::TrueColorMatteType :
30653       case Magick::ColorSeparationType : {
30654         assign(W,H,1,4);
30655         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
30656         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
30657         for (unsigned int off = W*H; off; --off) {
30658           *(ptr_r++) = (T)(pixels->red);
30659           *(ptr_g++) = (T)(pixels->green);
30660           *(ptr_b++) = (T)(pixels->blue);
30661           *(ptr_a++) = (T)(pixels->opacity);
30662           ++pixels;
30663         }
30664       } break;
30665       case Magick::PaletteType :
30666       case Magick::TrueColorType : {
30667         assign(W,H,1,3);
30668         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
30669         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
30670         for (unsigned int off = W*H; off; --off) {
30671           *(ptr_r++) = (T)(pixels->red);
30672           *(ptr_g++) = (T)(pixels->green);
30673           *(ptr_b++) = (T)(pixels->blue);
30674           ++pixels;
30675         }
30676       } break;
30677       case Magick::GrayscaleMatteType : {
30678         assign(W,H,1,2);
30679         T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
30680         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
30681         for (unsigned int off = W*H; off; --off) {
30682           *(ptr_r++) = (T)(pixels->red);
30683           *(ptr_a++) = (T)(pixels->opacity);
30684           ++pixels;
30685         }
30686       } break;
30687       default : {
30688         assign(W,H,1,1);
30689         T *ptr_r = data(0,0,0,0);
30690         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
30691         for (unsigned int off = W*H; off; --off) {
30692           *(ptr_r++) = (T)(pixels->red);
30693           ++pixels;
30694         }
30695       }
30696       }
30697 #else
30698       throw CImgIOException(_cimg_instance
30699                             "load_magick() : Unable to load file '%s' unless libMagick++ is enabled.",
30700                             cimg_instance,
30701                             filename);
30702 #endif
30703       return *this;
30704     }
30705 
30706     static CImg<T> get_load_magick(const char *const filename) {
30707       return CImg<T>().load_magick(filename);
30708     }
30709 
30710     //! Load an image from a PNG file.
30711     CImg<T>& load_png(const char *const filename) {
30712       return _load_png(0,filename);
30713     }
30714 
30715     static CImg<T> get_load_png(const char *const filename) {
30716       return CImg<T>().load_png(filename);
30717     }
30718 
30719     //! Load an image from a PNG file.
30720     CImg<T>& load_png(std::FILE *const file) {
30721       return _load_png(file,0);
30722     }
30723 
30724     static CImg<T> get_load_png(std::FILE *const file) {
30725       return CImg<T>().load_png(file);
30726     }
30727 
30728     // (Note : Most of this function has been written by Eric Fausett)
30729     CImg<T>& _load_png(std::FILE *const file, const char *const filename) {
30730       if (!file && !filename)
30731         throw CImgArgumentException(_cimg_instance
30732                                     "load_png() : Specified filename is (null).",
30733                                     cimg_instance);
30734 
30735 #ifndef cimg_use_png
30736       if (file)
30737         throw CImgIOException(_cimg_instance
30738                               "load_png() : Unable to load data from '(FILE*)' unless libpng is enabled.",
30739                               cimg_instance);
30740 
30741       else return load_other(filename);
30742 #else
30743       // Open file and check for PNG validity
30744       const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
30745       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
30746 
30747       unsigned char pngCheck[8] = { 0 };
30748       cimg::fread(pngCheck,8,(std::FILE*)nfile);
30749       if (png_sig_cmp(pngCheck,0,8)) {
30750         if (!file) cimg::fclose(nfile);
30751         throw CImgIOException(_cimg_instance
30752                               "load_png() : Invalid PNG file '%s'.",
30753                               cimg_instance,
30754                               nfilename?nfilename:"(FILE*)");
30755       }
30756 
30757       // Setup PNG structures for read
30758       png_voidp user_error_ptr = 0;
30759       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
30760       png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
30761       if (!png_ptr) {
30762         if (!file) cimg::fclose(nfile);
30763         throw CImgIOException(_cimg_instance
30764                               "load_png() : Failed to initialize 'png_ptr' structure for file '%s'.",
30765                               cimg_instance,
30766                               nfilename?nfilename:"(FILE*)");
30767       }
30768       png_infop info_ptr = png_create_info_struct(png_ptr);
30769       if (!info_ptr) {
30770         if (!file) cimg::fclose(nfile);
30771         png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
30772         throw CImgIOException(_cimg_instance
30773                               "load_png() : Failed to initialize 'info_ptr' structure for file '%s'.",
30774                               cimg_instance,
30775                               nfilename?nfilename:"(FILE*)");
30776       }
30777       png_infop end_info = png_create_info_struct(png_ptr);
30778       if (!end_info) {
30779         if (!file) cimg::fclose(nfile);
30780         png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
30781         throw CImgIOException(_cimg_instance
30782                               "load_png() : Failed to initialize 'end_info' structure for file '%s'.",
30783                               cimg_instance,
30784                               nfilename?nfilename:"(FILE*)");
30785       }
30786 
30787       // Error handling callback for png file reading
30788       if (setjmp(png_jmpbuf(png_ptr))) {
30789         if (!file) cimg::fclose((std::FILE*)nfile);
30790         png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
30791         throw CImgIOException(_cimg_instance
30792                               "load_png() : Encountered unknown fatal error in libpng for file '%s'.",
30793                               cimg_instance,
30794                               nfilename?nfilename:"(FILE*)");
30795       }
30796       png_init_io(png_ptr, nfile);
30797       png_set_sig_bytes(png_ptr, 8);
30798 
30799       // Get PNG Header Info up to data block
30800       png_read_info(png_ptr,info_ptr);
30801       png_uint_32 W, H;
30802       int bit_depth, color_type, interlace_type;
30803       png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
30804       int new_bit_depth = bit_depth;
30805       int new_color_type = color_type;
30806 
30807       // Transforms to unify image data
30808       if (new_color_type == PNG_COLOR_TYPE_PALETTE){
30809         png_set_palette_to_rgb(png_ptr);
30810         new_color_type-=PNG_COLOR_MASK_PALETTE;
30811         new_bit_depth = 8;
30812       }
30813       if (new_color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8){
30814         png_set_expand_gray_1_2_4_to_8(png_ptr);
30815         new_bit_depth = 8;
30816       }
30817       if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
30818         png_set_tRNS_to_alpha(png_ptr);
30819       if (new_color_type == PNG_COLOR_TYPE_GRAY || new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA){
30820         png_set_gray_to_rgb(png_ptr);
30821         new_color_type |= PNG_COLOR_MASK_COLOR;
30822       }
30823       if (new_color_type == PNG_COLOR_TYPE_RGB)
30824         png_set_filler(png_ptr, 0xffffU, PNG_FILLER_AFTER);
30825       png_read_update_info(png_ptr,info_ptr);
30826       if (!(new_bit_depth==8 || new_bit_depth==16)) {
30827         if (!file) cimg::fclose(nfile);
30828         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
30829         throw CImgIOException(_cimg_instance
30830                               "load_png() : Invalid bit depth %u in file '%s'.",
30831                               cimg_instance,
30832                               new_bit_depth,nfilename?nfilename:"(FILE*)");
30833       }
30834       const int byte_depth = new_bit_depth>>3;
30835 
30836       // Allocate Memory for Image Read
30837       png_bytep *const imgData = new png_bytep[H];
30838       for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[byte_depth*4*W];
30839       png_read_image(png_ptr,imgData);
30840       png_read_end(png_ptr,end_info);
30841 
30842       // Read pixel data
30843       if (!(new_color_type==PNG_COLOR_TYPE_RGB || new_color_type==PNG_COLOR_TYPE_RGB_ALPHA)) {
30844         if (!file) cimg::fclose(nfile);
30845         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
30846         throw CImgIOException(_cimg_instance
30847                               "load_png() : Invalid color coding type %u in file '%s'.",
30848                               cimg_instance,
30849                               new_color_type,nfilename?nfilename:"(FILE*)");
30850       }
30851       const bool no_alpha_channel = (new_color_type==PNG_COLOR_TYPE_RGB);
30852       assign(W,H,1,no_alpha_channel?3:4);
30853       T *ptr1 = data(0,0,0,0), *ptr2 = data(0,0,0,1), *ptr3 = data(0,0,0,2), *ptr4 = data(0,0,0,3);
30854       switch (new_bit_depth) {
30855       case 8 : {
30856         cimg_forY(*this,y){
30857           const unsigned char *ptrs = (unsigned char*)imgData[y];
30858           cimg_forX(*this,x){
30859             *(ptr1++) = (T)*(ptrs++);
30860             *(ptr2++) = (T)*(ptrs++);
30861             *(ptr3++) = (T)*(ptrs++);
30862             if (no_alpha_channel) ++ptrs; else *(ptr4++) = (T)*(ptrs++);
30863           }
30864         }
30865       } break;
30866       case 16 : {
30867         cimg_forY(*this,y){
30868           const unsigned short *ptrs = (unsigned short*)(imgData[y]);
30869           if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
30870           cimg_forX(*this,x){
30871             *(ptr1++) = (T)*(ptrs++);
30872             *(ptr2++) = (T)*(ptrs++);
30873             *(ptr3++) = (T)*(ptrs++);
30874             if (no_alpha_channel) ++ptrs; else *(ptr4++) = (T)*(ptrs++);
30875           }
30876         }
30877       } break;
30878       }
30879       png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
30880 
30881       // Deallocate Image Read Memory
30882       cimg_forY(*this,n) delete[] imgData[n];
30883       delete[] imgData;
30884       if (!file) cimg::fclose(nfile);
30885       return *this;
30886 #endif
30887     }
30888 
30889     //! Load an image from a PNM file.
30890     CImg<T>& load_pnm(const char *const filename) {
30891       return _load_pnm(0,filename);
30892     }
30893 
30894     static CImg<T> get_load_pnm(const char *const filename) {
30895       return CImg<T>().load_pnm(filename);
30896     }
30897 
30898     //! Load an image from a PNM file.
30899     CImg<T>& load_pnm(std::FILE *const file) {
30900       return _load_pnm(file,0);
30901     }
30902 
30903     static CImg<T> get_load_pnm(std::FILE *const file) {
30904       return CImg<T>().load_pnm(file);
30905     }
30906 
30907     CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
30908       if (!file && !filename)
30909         throw CImgArgumentException(_cimg_instance
30910                                     "load_pnm() : Specified filename is (null).",
30911                                     cimg_instance);
30912 
30913       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
30914       unsigned int ppm_type, W, H, colormax = 255;
30915       char item[1024] = { 0 };
30916       int err, rval, gval, bval;
30917       const int cimg_iobuffer = 12*1024*1024;
30918       while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile);
30919       if (std::sscanf(item," P%u",&ppm_type)!=1) {
30920         if (!file) cimg::fclose(nfile);
30921         throw CImgIOException(_cimg_instance
30922                               "load_pnm() : PNM header not found in file '%s'.",
30923                               cimg_instance,
30924                               filename?filename:"(FILE*)");
30925       }
30926       while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile);
30927       if ((err=std::sscanf(item," %u %u %u",&W,&H,&colormax))<2) {
30928         if (!file) cimg::fclose(nfile);
30929         throw CImgIOException(_cimg_instance
30930                               "load_pnm() : WIDTH and HEIGHT fields undefined in file '%s'.",
30931                               cimg_instance,
30932                               filename?filename:"(FILE*)");
30933       }
30934       if (err==2) {
30935         while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile);
30936         if (std::sscanf(item,"%u",&colormax)!=1)
30937           cimg::warn(_cimg_instance
30938                      "load_pnm() : COLORMAX field is undefined in file '%s'.",
30939                      cimg_instance,
30940                      filename?filename:"(FILE*)");
30941       }
30942       std::fgetc(nfile);
30943 
30944       switch (ppm_type) {
30945       case 2 : { // Grey Ascii
30946         assign(W,H,1,1);
30947         T* ptr_r = _data;
30948         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptr_r++) = (T)rval; else break; }
30949       } break;
30950       case 3 : { // Color Ascii
30951         assign(W,H,1,3);
30952         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
30953         cimg_forXY(*this,x,y) {
30954           if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { *(ptr_r++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval; }
30955           else break;
30956         }
30957       } break;
30958       case 5 : { // Grey Binary
30959         if (colormax<256) { // 8 bits
30960           CImg<ucharT> raw;
30961           assign(W,H,1,1);
30962           T *ptrd = data(0,0,0,0);
30963           for (int toread = (int)size(); toread>0; ) {
30964             raw.assign(cimg::min(toread,cimg_iobuffer));
30965             cimg::fread(raw._data,raw._width,nfile);
30966             toread-=raw._width;
30967             const unsigned char *ptrs = raw._data;
30968             for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
30969           }
30970         } else { // 16 bits
30971           CImg<ushortT> raw;
30972           assign(W,H,1,1);
30973           T *ptrd = data(0,0,0,0);
30974           for (int toread = (int)size(); toread>0; ) {
30975             raw.assign(cimg::min(toread,cimg_iobuffer/2));
30976             cimg::fread(raw._data,raw._width,nfile);
30977             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
30978             toread-=raw._width;
30979             const unsigned short *ptrs = raw._data;
30980             for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
30981           }
30982         }
30983       } break;
30984       case 6 : { // Color Binary
30985         if (colormax<256) { // 8 bits
30986           CImg<ucharT> raw;
30987           assign(W,H,1,3);
30988           T
30989             *ptr_r = data(0,0,0,0),
30990             *ptr_g = data(0,0,0,1),
30991             *ptr_b = data(0,0,0,2);
30992           for (int toread = (int)size(); toread>0; ) {
30993             raw.assign(cimg::min(toread,cimg_iobuffer));
30994             cimg::fread(raw._data,raw._width,nfile);
30995             toread-=raw._width;
30996             const unsigned char *ptrs = raw._data;
30997             for (unsigned int off = raw._width/3; off; --off) {
30998               *(ptr_r++) = (T)*(ptrs++);
30999               *(ptr_g++) = (T)*(ptrs++);
31000               *(ptr_b++) = (T)*(ptrs++);
31001             }
31002           }
31003         } else { // 16 bits
31004           CImg<ushortT> raw;
31005           assign(W,H,1,3);
31006           T
31007             *ptr_r = data(0,0,0,0),
31008             *ptr_g = data(0,0,0,1),
31009             *ptr_b = data(0,0,0,2);
31010           for (int toread = (int)size(); toread>0; ) {
31011             raw.assign(cimg::min(toread,cimg_iobuffer/2));
31012             cimg::fread(raw._data,raw._width,nfile);
31013             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
31014             toread-=raw._width;
31015             const unsigned short *ptrs = raw._data;
31016             for (unsigned int off = raw._width/3; off; --off) {
31017               *(ptr_r++) = (T)*(ptrs++);
31018               *(ptr_g++) = (T)*(ptrs++);
31019               *(ptr_b++) = (T)*(ptrs++);
31020             }
31021           }
31022         }
31023       } break;
31024       default :
31025         assign();
31026         if (!file) cimg::fclose(nfile);
31027         throw CImgIOException(_cimg_instance
31028                               "load_pnm() : PNM type 'P%d' found, but type is not supported.",
31029                               cimg_instance,
31030                               filename?filename:"(FILE*)",ppm_type);
31031       }
31032       if (!file) cimg::fclose(nfile);
31033       return *this;
31034     }
31035 
31036     //! Load an image from a PFM file.
31037     CImg<T>& load_pfm(const char *const filename) {
31038       return _load_pfm(0,filename);
31039     }
31040 
31041     static CImg<T> get_load_pfm(const char *const filename) {
31042       return CImg<T>().load_pfm(filename);
31043     }
31044 
31045     //! Load an image from a PFM file.
31046     CImg<T>& load_pfm(std::FILE *const file) {
31047       return _load_pfm(file,0);
31048     }
31049 
31050     static CImg<T> get_load_pfm(std::FILE *const file) {
31051       return CImg<T>().load_pfm(file);
31052     }
31053 
31054     CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
31055       if (!file && !filename)
31056         throw CImgArgumentException(_cimg_instance
31057                                     "load_pfm() : Specified filename is (null).",
31058                                     cimg_instance);
31059 
31060       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
31061       char pfm_type, item[1024] = { 0 };
31062       int W = 0, H = 0, err = 0;
31063       double scale = 0;
31064       while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile);
31065       if (std::sscanf(item," P%c",&pfm_type)!=1) {
31066         if (!file) cimg::fclose(nfile);
31067         throw CImgIOException(_cimg_instance
31068                               "load_pfm() : PFM header not found in file '%s'.",
31069                               cimg_instance,
31070                               filename?filename:"(FILE*)");
31071       }
31072       while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile);
31073       if ((err=std::sscanf(item," %d %d",&W,&H))<2) {
31074         if (!file) cimg::fclose(nfile);
31075         throw CImgIOException(_cimg_instance
31076                               "load_pfm() : WIDTH and HEIGHT fields are undefined in file '%s'.",
31077                               cimg_instance,
31078                               filename?filename:"(FILE*)");
31079       }
31080       if (err==2) {
31081         while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile);
31082         if (std::sscanf(item,"%lf",&scale)!=1)
31083           cimg::warn(_cimg_instance
31084                      "load_pfm() : SCALE field is undefined in file '%s'.",
31085                      cimg_instance,
31086                      filename?filename:"(FILE*)");
31087       }
31088       std::fgetc(nfile);
31089       const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
31090       if (is_color) {
31091         assign(W,H,1,3,0);
31092         CImg<floatT> buf(3*W);
31093         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
31094         cimg_forY(*this,y) {
31095           cimg::fread(buf._data,3*W,nfile);
31096           if (is_inverted) cimg::invert_endianness(buf._data,3*W);
31097           const float *ptrs = buf._data;
31098           cimg_forX(*this,x) {
31099             *(ptr_r++) = (T)*(ptrs++);
31100             *(ptr_g++) = (T)*(ptrs++);
31101             *(ptr_b++) = (T)*(ptrs++);
31102           }
31103         }
31104       } else {
31105         assign(W,H,1,1,0);
31106         CImg<floatT> buf(W);
31107         T *ptrd = data(0,0,0,0);
31108         cimg_forY(*this,y) {
31109           cimg::fread(buf._data,W,nfile);
31110           if (is_inverted) cimg::invert_endianness(buf._data,W);
31111           const float *ptrs = buf._data;
31112           cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
31113         }
31114       }
31115       if (!file) cimg::fclose(nfile);
31116       return *this;
31117     }
31118 
31119     //! Load an image from a RGB file.
31120     CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
31121       return _load_rgb(0,filename,dimw,dimh);
31122     }
31123 
31124     static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
31125       return CImg<T>().load_rgb(filename,dimw,dimh);
31126     }
31127 
31128     //! Load an image from a RGB file.
31129     CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
31130       return _load_rgb(file,0,dimw,dimh);
31131     }
31132 
31133     static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
31134       return CImg<T>().load_rgb(file,dimw,dimh);
31135     }
31136 
31137     CImg<T>& _load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) {
31138       if (!file && !filename)
31139         throw CImgArgumentException(_cimg_instance
31140                                     "load_rgb() : Specified filename is (null).",
31141                                     cimg_instance);
31142 
31143       if (!dimw || !dimh) return assign();
31144       const int cimg_iobuffer = 12*1024*1024;
31145       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
31146       CImg<ucharT> raw;
31147       assign(dimw,dimh,1,3);
31148       T
31149         *ptr_r = data(0,0,0,0),
31150         *ptr_g = data(0,0,0,1),
31151         *ptr_b = data(0,0,0,2);
31152       for (int toread = (int)size(); toread>0; ) {
31153         raw.assign(cimg::min(toread,cimg_iobuffer));
31154         cimg::fread(raw._data,raw._width,nfile);
31155         toread-=raw._width;
31156         const unsigned char *ptrs = raw._data;
31157         for (unsigned int off = raw._width/3; off; --off) {
31158           *(ptr_r++) = (T)*(ptrs++);
31159           *(ptr_g++) = (T)*(ptrs++);
31160           *(ptr_b++) = (T)*(ptrs++);
31161         }
31162       }
31163       if (!file) cimg::fclose(nfile);
31164       return *this;
31165     }
31166 
31167     //! Load an image from a RGBA file.
31168     CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
31169       return _load_rgba(0,filename,dimw,dimh);
31170     }
31171 
31172     static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
31173       return CImg<T>().load_rgba(filename,dimw,dimh);
31174     }
31175 
31176     //! Load an image from a RGBA file.
31177     CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
31178       return _load_rgba(file,0,dimw,dimh);
31179     }
31180 
31181     static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
31182       return CImg<T>().load_rgba(file,dimw,dimh);
31183     }
31184 
31185     CImg<T>& _load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) {
31186       if (!file && !filename)
31187         throw CImgArgumentException(_cimg_instance
31188                                     "load_rgba() : Specified filename is (null).",
31189                                     cimg_instance);
31190 
31191       if (!dimw || !dimh) return assign();
31192       const int cimg_iobuffer = 12*1024*1024;
31193       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
31194       CImg<ucharT> raw;
31195       assign(dimw,dimh,1,4);
31196       T
31197         *ptr_r = data(0,0,0,0),
31198         *ptr_g = data(0,0,0,1),
31199         *ptr_b = data(0,0,0,2),
31200         *ptr_a = data(0,0,0,3);
31201       for (int toread = (int)size(); toread>0; ) {
31202         raw.assign(cimg::min(toread,cimg_iobuffer));
31203         cimg::fread(raw._data,raw._width,nfile);
31204         toread-=raw._width;
31205         const unsigned char *ptrs = raw._data;
31206         for (unsigned int off = raw._width/4; off; --off) {
31207           *(ptr_r++) = (T)*(ptrs++);
31208           *(ptr_g++) = (T)*(ptrs++);
31209           *(ptr_b++) = (T)*(ptrs++);
31210           *(ptr_a++) = (T)*(ptrs++);
31211         }
31212       }
31213       if (!file) cimg::fclose(nfile);
31214       return *this;
31215     }
31216 
31217     //! Load an image from a TIFF file.
31218     CImg<T>& load_tiff(const char *const filename,
31219                        const unsigned int first_frame=0, const unsigned int last_frame=~0U,
31220                        const unsigned int step_frame=1) {
31221       if (!filename)
31222         throw CImgArgumentException(_cimg_instance
31223                                     "load_tiff() : Specified filename is (null).",
31224                                     cimg_instance);
31225 
31226       const unsigned int
31227         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
31228         nstep_frame = step_frame?step_frame:1;
31229       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
31230 
31231 #ifndef cimg_use_tiff
31232       if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
31233         throw CImgArgumentException(_cimg_instance
31234                                     "load_tiff() : Unable to read sub-images from file '%s' unless libtiff is enabled.",
31235                                     cimg_instance,
31236                                     filename);
31237       return load_other(filename);
31238 #else
31239       TIFF *tif = TIFFOpen(filename,"r");
31240       if (tif) {
31241         unsigned int nb_images = 0;
31242         do ++nb_images; while (TIFFReadDirectory(tif));
31243         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
31244           cimg::warn(_cimg_instance
31245                      "load_tiff() : File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
31246                      cimg_instance,
31247                      filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
31248 
31249         if (nfirst_frame>=nb_images) return assign();
31250         if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
31251         TIFFSetDirectory(tif,0);
31252         CImg<T> frame;
31253         for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
31254           frame._load_tiff(tif,l);
31255           if (l==nfirst_frame) assign(frame._width,frame._height,1+(nlast_frame-nfirst_frame)/nstep_frame,frame._spectrum);
31256           if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
31257             resize(cimg::max(frame._width,_width),cimg::max(frame._height,_height),-100,cimg::max(frame._spectrum,_spectrum),0);
31258           draw_image(0,0,(l-nfirst_frame)/nstep_frame,frame);
31259         }
31260         TIFFClose(tif);
31261       } else throw CImgException(_cimg_instance
31262                                  "load_tiff() : Failed to open file '%s'.",
31263                                  cimg_instance,
31264                                  filename);
31265       return *this;
31266 #endif
31267     }
31268 
31269     static CImg<T> get_load_tiff(const char *const filename,
31270                                  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
31271                                  const unsigned int step_frame=1) {
31272       return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame);
31273     }
31274 
31275     // (Original contribution by Jerome Boulanger).
31276 #ifdef cimg_use_tiff
31277     template<typename t>
31278     void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
31279       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
31280       if (buf) {
31281         for (unsigned int row = 0; row<ny; row+=th)
31282           for (unsigned int col = 0; col<nx; col+=tw) {
31283             if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
31284               _TIFFfree(buf); TIFFClose(tif);
31285               throw CImgException(_cimg_instance
31286                                   "load_tiff() : Invalid tile in file '%s'.",
31287                                   cimg_instance,
31288                                   TIFFFileName(tif));
31289             }
31290             const t *ptr = buf;
31291             for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
31292               for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
31293                 for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
31294                   (*this)(cc,rr,vv) = (T)(ptr[(rr-row)*th*samplesperpixel + (cc-col)*samplesperpixel + vv]);
31295           }
31296         _TIFFfree(buf);
31297       }
31298     }
31299 
31300     template<typename t>
31301     void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
31302       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
31303       if (buf) {
31304         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
31305           for (unsigned int row = 0; row<ny; row+=th)
31306             for (unsigned int col = 0; col<nx; col+=tw) {
31307               if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
31308                 _TIFFfree(buf); TIFFClose(tif);
31309                 throw CImgException(_cimg_instance
31310                                     "load_tiff() : Invalid tile in file '%s'.",
31311                                     cimg_instance,
31312                                     TIFFFileName(tif));
31313               }
31314               const t *ptr = buf;
31315               for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
31316                 for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
31317                   (*this)(cc,rr,vv) = (T)*(ptr++);
31318             }
31319         _TIFFfree(buf);
31320       }
31321     }
31322 
31323     template<typename t>
31324     void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
31325       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
31326       if (buf) {
31327         uint32 row, rowsperstrip = (uint32)-1;
31328         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
31329         for (row = 0; row<ny; row+= rowsperstrip) {
31330           uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
31331           tstrip_t strip = TIFFComputeStrip(tif, row, 0);
31332           if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
31333             _TIFFfree(buf); TIFFClose(tif);
31334             throw CImgException(_cimg_instance
31335                                 "load_tiff() : Invalid strip in file '%s'.",
31336                                 cimg_instance,
31337                                 TIFFFileName(tif));
31338           }
31339           const t *ptr = buf;
31340           for (unsigned int rr = 0; rr<nrow; ++rr)
31341             for (unsigned int cc = 0; cc<nx; ++cc)
31342               for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row+rr,vv) = (T)*(ptr++);
31343         }
31344         _TIFFfree(buf);
31345       }
31346     }
31347 
31348     template<typename t>
31349     void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
31350       t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
31351       if (buf) {
31352         uint32 row, rowsperstrip = (uint32)-1;
31353         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
31354         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
31355           for (row = 0; row<ny; row+= rowsperstrip) {
31356             uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
31357             tstrip_t strip = TIFFComputeStrip(tif, row, vv);
31358             if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
31359               _TIFFfree(buf); TIFFClose(tif);
31360               throw CImgException(_cimg_instance
31361                                   "load_tiff() : Invalid strip in file '%s'.",
31362                                   cimg_instance,
31363                                   TIFFFileName(tif));
31364             }
31365             const t *ptr = buf;
31366             for (unsigned int rr = 0;rr<nrow; ++rr)
31367               for (unsigned int cc = 0; cc<nx; ++cc)
31368                 (*this)(cc,row+rr,vv) = (T)*(ptr++);
31369           }
31370         _TIFFfree(buf);
31371       }
31372     }
31373 
31374     CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory) {
31375       if (!TIFFSetDirectory(tif,directory)) return assign();
31376       uint16 samplesperpixel, bitspersample;
31377       uint16 sampleformat = SAMPLEFORMAT_UINT;
31378       uint32 nx,ny;
31379       const char *const filename = TIFFFileName(tif);
31380       TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
31381       TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
31382       TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
31383       TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
31384       if (samplesperpixel!=1 && samplesperpixel!=3 && samplesperpixel!=4) {
31385         cimg::warn(_cimg_instance
31386                    "load_tiff() : Unknow value for tag TIFFTAG_SAMPLESPERPIXEL (so assumed to be 1), in file '%s'.",
31387                    cimg_instance,
31388                    filename);
31389 
31390         samplesperpixel = 1;
31391       }
31392       TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
31393       assign(nx,ny,1,samplesperpixel);
31394       if (bitspersample!=8 || !(samplesperpixel==3 || samplesperpixel==4)) {
31395         uint16 photo, config;
31396         TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
31397         TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
31398         if (TIFFIsTiled(tif)) {
31399           uint32 tw, th;
31400           TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
31401           TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
31402           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
31403             case 8 : {
31404               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
31405               else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
31406             } break;
31407             case 16 :
31408               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
31409               else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
31410               break;
31411             case 32 :
31412               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
31413               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
31414               else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
31415               break;
31416             } else switch (bitspersample) {
31417             case 8 :
31418               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
31419               else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
31420               break;
31421             case 16 :
31422               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
31423               else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
31424               break;
31425             case 32 :
31426               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
31427               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
31428               else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
31429               break;
31430             }
31431         } else {
31432           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
31433             case 8 :
31434               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
31435               else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
31436               break;
31437             case 16 :
31438               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
31439               else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
31440               break;
31441             case 32 :
31442               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
31443               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
31444               else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
31445               break;
31446             } else switch (bitspersample){
31447             case 8 :
31448               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
31449               else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
31450               break;
31451             case 16 :
31452               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
31453               else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
31454               break;
31455             case 32 :
31456               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
31457               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
31458               else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
31459               break;
31460             }
31461         }
31462       } else {
31463         uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32));
31464         if (!raster) {
31465           _TIFFfree(raster); TIFFClose(tif);
31466           throw CImgException(_cimg_instance
31467                               "load_tiff() : Failed to allocated memory for file '%s'.",
31468                               cimg_instance,
31469                               filename);
31470         }
31471         TIFFReadRGBAImage(tif,nx,ny,raster,0);
31472         switch (samplesperpixel) {
31473         case 1 : {
31474           cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny-1-y)+x] + 128)/257);
31475         } break;
31476         case 3 : {
31477           cimg_forXY(*this,x,y) {
31478             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
31479             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
31480             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
31481           }
31482         } break;
31483         case 4 : {
31484           cimg_forXY(*this,x,y) {
31485             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
31486             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
31487             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
31488             (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny-1-y)+x]);
31489           }
31490         } break;
31491         }
31492         _TIFFfree(raster);
31493       }
31494       return *this;
31495     }
31496 #endif
31497 
31498     //! Load an image from an ANALYZE7.5/NIFTI file.
31499     CImg<T>& load_analyze(const char *const filename, float *const voxsize=0) {
31500       return _load_analyze(0,filename,voxsize);
31501     }
31502 
31503     static CImg<T> get_load_analyze(const char *const filename, float *const voxsize=0) {
31504       return CImg<T>().load_analyze(filename,voxsize);
31505     }
31506 
31507     //! Load an image from an ANALYZE7.5/NIFTI file.
31508     CImg<T>& load_analyze(std::FILE *const file, float *const voxsize=0) {
31509       return _load_analyze(file,0,voxsize);
31510     }
31511 
31512     static CImg<T> get_load_analyze(std::FILE *const file, float *const voxsize=0) {
31513       return CImg<T>().load_analyze(file,voxsize);
31514     }
31515 
31516     CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxsize=0) {
31517       if (!file && !filename)
31518         throw CImgArgumentException(_cimg_instance
31519                                     "load_analyze() : Specified filename is (null).",
31520                                     cimg_instance);
31521 
31522       std::FILE *nfile_header = 0, *nfile = 0;
31523       if (!file) {
31524         char body[1024] = { 0 };
31525         const char *const ext = cimg::split_filename(filename,body);
31526         if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file.
31527           nfile_header = cimg::fopen(filename,"rb");
31528           std::sprintf(body+std::strlen(body),".img");
31529           nfile = cimg::fopen(body,"rb");
31530         } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file.
31531           nfile = cimg::fopen(filename,"rb");
31532           std::sprintf(body+std::strlen(body),".hdr");
31533           nfile_header = cimg::fopen(body,"rb");
31534         } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file.
31535       } else nfile_header = nfile = file; // File is a Niftii file.
31536       if (!nfile || !nfile_header)
31537         throw CImgIOException(_cimg_instance
31538                               "load_analyze() : Invalid Analyze7.5 or NIFTI header in file '%s'.",
31539                               cimg_instance,
31540                               filename?filename:"(FILE*)");
31541 
31542       // Read header.
31543       bool endian = false;
31544       unsigned int header_size;
31545       cimg::fread(&header_size,1,nfile_header);
31546       if (!header_size)
31547         throw CImgIOException(_cimg_instance
31548                               "load_analyze() : Invalid zero-sized header in file '%s'.",
31549                               cimg_instance,
31550                               filename?filename:"(FILE*)");
31551 
31552       if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
31553       unsigned char *const header = new unsigned char[header_size];
31554       cimg::fread(header+4,header_size-4,nfile_header);
31555       if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
31556       if (endian) {
31557         cimg::invert_endianness((short*)(header+40),5);
31558         cimg::invert_endianness((short*)(header+70),1);
31559         cimg::invert_endianness((short*)(header+72),1);
31560         cimg::invert_endianness((float*)(header+76),4);
31561         cimg::invert_endianness((float*)(header+112),1);
31562       }
31563       unsigned short *dim = (unsigned short*)(header+40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
31564       if (!dim[0])
31565         cimg::warn(_cimg_instance
31566                    "load_analyze() : File '%s' defines an image with zero dimensions.",
31567                    cimg_instance,
31568                    filename?filename:"(FILE*)");
31569 
31570       if (dim[0]>4)
31571         cimg::warn(_cimg_instance
31572                    "load_analyze() : File '%s' defines an image with %u dimensions, reading only the 4 first.",
31573                    cimg_instance,
31574                    filename?filename:"(FILE*)",dim[0]);
31575 
31576       if (dim[0]>=1) dimx = dim[1];
31577       if (dim[0]>=2) dimy = dim[2];
31578       if (dim[0]>=3) dimz = dim[3];
31579       if (dim[0]>=4) dimv = dim[4];
31580       float scalefactor = *(float*)(header+112); if (scalefactor==0) scalefactor=1;
31581       const unsigned short datatype = *(short*)(header+70);
31582       if (voxsize) {
31583         const float *vsize = (float*)(header+76);
31584         voxsize[0] = vsize[1]; voxsize[1] = vsize[2]; voxsize[2] = vsize[3];
31585       }
31586       delete[] header;
31587 
31588       // Read pixel data.
31589       assign(dimx,dimy,dimz,dimv);
31590       switch (datatype) {
31591       case 2 : {
31592         unsigned char *const buffer = new unsigned char[dimx*dimy*dimz*dimv];
31593         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
31594         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
31595         delete[] buffer;
31596       } break;
31597       case 4 : {
31598         short *const buffer = new short[dimx*dimy*dimz*dimv];
31599         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
31600         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
31601         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
31602         delete[] buffer;
31603       } break;
31604       case 8 : {
31605         int *const buffer = new int[dimx*dimy*dimz*dimv];
31606         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
31607         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
31608         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
31609         delete[] buffer;
31610       } break;
31611       case 16 : {
31612         float *const buffer = new float[dimx*dimy*dimz*dimv];
31613         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
31614         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
31615         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
31616         delete[] buffer;
31617       } break;
31618       case 64 : {
31619         double *const buffer = new double[dimx*dimy*dimz*dimv];
31620         cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
31621         if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
31622         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
31623         delete[] buffer;
31624       } break;
31625       default :
31626         if (!file) cimg::fclose(nfile);
31627         throw CImgIOException(_cimg_instance
31628                               "load_analyze() : Unable to load datatype %d in file '%s'",
31629                               cimg_instance,
31630                               datatype,filename?filename:"(FILE*)");
31631       }
31632       if (!file) cimg::fclose(nfile);
31633       return *this;
31634     }
31635 
31636     //! Load an image (list) from a .cimg file.
31637     CImg<T>& load_cimg(const char *const filename, const char axis='z', const char align='p') {
31638       CImgList<T> list;
31639       list.load_cimg(filename);
31640       if (list._width==1) return list[0].move_to(*this);
31641       return assign(list.get_append(axis,align));
31642     }
31643 
31644     static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const char align='p') {
31645       return CImg<T>().load_cimg(filename,axis,align);
31646     }
31647 
31648     //! Load an image (list) from a .cimg file.
31649     CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const char align='p') {
31650       CImgList<T> list;
31651       list.load_cimg(file);
31652       if (list._width==1) return list[0].move_to(*this);
31653       return assign(list.get_append(axis,align));
31654     }
31655 
31656     static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const char align='p') {
31657       return CImg<T>().load_cimg(file,axis,align);
31658     }
31659 
31660     //! Load a sub-image (list) from a .cimg file.
31661     CImg<T>& load_cimg(const char *const filename,
31662                        const unsigned int n0, const unsigned int n1,
31663                        const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
31664                        const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
31665                        const char axis='z', const char align='p') {
31666       CImgList<T> list;
31667       list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
31668       if (list._width==1) return list[0].move_to(*this);
31669       return assign(list.get_append(axis,align));
31670     }
31671 
31672     static CImg<T> get_load_cimg(const char *const filename,
31673                                  const unsigned int n0, const unsigned int n1,
31674                                  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
31675                                  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
31676                                  const char axis='z', const char align='p') {
31677       return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
31678     }
31679 
31680     //! Load a sub-image (list) from a non-compressed .cimg file.
31681     CImg<T>& load_cimg(std::FILE *const file,
31682                        const unsigned int n0, const unsigned int n1,
31683                        const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
31684                        const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
31685                        const char axis='z', const char align='p') {
31686       CImgList<T> list;
31687       list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
31688       if (list._width==1) return list[0].move_to(*this);
31689       return assign(list.get_append(axis,align));
31690     }
31691 
31692     static CImg<T> get_load_cimg(std::FILE *const file,
31693                                  const unsigned int n0, const unsigned int n1,
31694                                  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
31695                                  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
31696                                  const char axis='z', const char align='p') {
31697       return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
31698     }
31699 
31700     //! Load an image from an INRIMAGE-4 file.
31701     CImg<T>& load_inr(const char *const filename, float *const voxsize=0) {
31702       return _load_inr(0,filename,voxsize);
31703     }
31704 
31705     static CImg<T> get_load_inr(const char *const filename, float *const voxsize=0) {
31706       return CImg<T>().load_inr(filename,voxsize);
31707     }
31708 
31709     //! Load an image from an INRIMAGE-4 file.
31710     CImg<T>& load_inr(std::FILE *const file, float *const voxsize=0) {
31711       return _load_inr(file,0,voxsize);
31712     }
31713 
31714     static CImg<T> get_load_inr(std::FILE *const file, float *voxsize=0) {
31715       return CImg<T>().load_inr(file,voxsize);
31716     }
31717 
31718     // Load an image from an INRIMAGE-4 file (internal).
31719     static void _load_inr_header(std::FILE *file, int out[8], float *const voxsize) {
31720       char item[1024] = { 0 }, tmp1[64] = { 0 }, tmp2[64] = { 0 };
31721       out[0] = std::fscanf(file,"%63s",item);
31722       out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
31723       if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
31724         throw CImgIOException("CImg<%s>::load_inr() : INRIMAGE-4 header not found.",
31725                               pixel_type());
31726 
31727       while (std::fscanf(file," %63[^\n]%*c",item)!=EOF && std::strncmp(item,"##}",3)) {
31728         std::sscanf(item," XDIM%*[^0-9]%d",out);
31729         std::sscanf(item," YDIM%*[^0-9]%d",out+1);
31730         std::sscanf(item," ZDIM%*[^0-9]%d",out+2);
31731         std::sscanf(item," VDIM%*[^0-9]%d",out+3);
31732         std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6);
31733         if (voxsize) {
31734           std::sscanf(item," VX%*[^0-9.+-]%f",voxsize);
31735           std::sscanf(item," VY%*[^0-9.+-]%f",voxsize+1);
31736           std::sscanf(item," VZ%*[^0-9.+-]%f",voxsize+2);
31737         }
31738         if (std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1;
31739         switch (std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) {
31740         case 0 : break;
31741         case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strcpy(tmp1,tmp2);
31742         case 1 :
31743           if (!cimg::strncasecmp(tmp1,"int",3)   || !cimg::strncasecmp(tmp1,"fixed",5))  out[4] = 0;
31744           if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
31745           if (!cimg::strncasecmp(tmp1,"packed",6))                                       out[4] = 2;
31746           if (out[4]>=0) break;
31747         default :
31748           throw CImgIOException("CImg<%s>::load_inr() : Invalid pixel type '%s' defined in header.",
31749                                 pixel_type(),
31750                                 tmp2);
31751         }
31752       }
31753       if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
31754         throw CImgIOException("CImg<%s>::load_inr() : Invalid dimensions (%d,%d,%d,%d) defined in header.",
31755                               pixel_type(),
31756                               out[0],out[1],out[2],out[3]);
31757       if(out[4]<0 || out[5]<0)
31758         throw CImgIOException("CImg<%s>::load_inr() : Incomplete pixel type defined in header.",
31759                               pixel_type());
31760       if(out[6]<0)
31761         throw CImgIOException("CImg<%s>::load_inr() : Incomplete PIXSIZE field defined in header.",
31762                               pixel_type());
31763       if(out[7]<0)
31764         throw CImgIOException("CImg<%s>::load_inr() : Big/Little Endian coding type undefined in header.",
31765                               pixel_type());
31766     }
31767 
31768     CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxsize) {
31769 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
31770      if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
31771         Ts *xval, *const val = new Ts[fopt[0]*fopt[3]]; \
31772         cimg_forYZ(*this,y,z) { \
31773             cimg::fread(val,fopt[0]*fopt[3],nfile); \
31774             if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
31775             xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
31776           } \
31777         delete[] val; \
31778         loaded = true; \
31779       }
31780 
31781       if (!file && !filename)
31782         throw CImgArgumentException(_cimg_instance
31783                                     "load_inr() : Specified filename is (null).",
31784                                     cimg_instance);
31785 
31786       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
31787       int fopt[8], endian=cimg::endianness()?1:0;
31788       bool loaded = false;
31789       if (voxsize) voxsize[0]=voxsize[1]=voxsize[2]=1;
31790       _load_inr_header(nfile,fopt,voxsize);
31791       assign(fopt[0],fopt[1],fopt[2],fopt[3]);
31792       _cimg_load_inr_case(0,0,8,unsigned char);
31793       _cimg_load_inr_case(0,1,8,char);
31794       _cimg_load_inr_case(0,0,16,unsigned short);
31795       _cimg_load_inr_case(0,1,16,short);
31796       _cimg_load_inr_case(0,0,32,unsigned int);
31797       _cimg_load_inr_case(0,1,32,int);
31798       _cimg_load_inr_case(1,0,32,float);
31799       _cimg_load_inr_case(1,1,32,float);
31800       _cimg_load_inr_case(1,0,64,double);
31801       _cimg_load_inr_case(1,1,64,double);
31802       if (!loaded) {
31803         if (!file) cimg::fclose(nfile);
31804         throw CImgIOException(_cimg_instance
31805                               "load_inr() : Unknown pixel type defined in file '%s'.",
31806                               cimg_instance,
31807                               filename?filename:"(FILE*)");
31808       }
31809       if (!file) cimg::fclose(nfile);
31810       return *this;
31811     }
31812 
31813     //! Load an image from a EXR file.
31814     CImg<T>& load_exr(const char *const filename) {
31815       if (!filename)
31816         throw CImgArgumentException(_cimg_instance
31817                                     "load_exr() : Specified filename is (null).",
31818                                     cimg_instance);
31819 
31820 #ifndef cimg_use_openexr
31821       return load_other(filename);
31822 #else
31823       Imf::RgbaInputFile file(filename);
31824       Imath::Box2i dw = file.dataWindow();
31825       const int
31826         inwidth = dw.max.x - dw.min.x + 1,
31827         inheight = dw.max.y - dw.min.y + 1;
31828       Imf::Array2D<Imf::Rgba> pixels;
31829       pixels.resizeErase(inheight,inwidth);
31830       file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
31831       file.readPixels(dw.min.y, dw.max.y);
31832       assign(inwidth,inheight,1,4);
31833       T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
31834       cimg_forXY(*this,x,y) {
31835         *(ptr_r++) = (T)pixels[y][x].r;
31836         *(ptr_g++) = (T)pixels[y][x].g;
31837         *(ptr_b++) = (T)pixels[y][x].b;
31838         *(ptr_a++) = (T)pixels[y][x].a;
31839       }
31840       return *this;
31841 #endif
31842     }
31843 
31844     static CImg<T> get_load_exr(const char *const filename) {
31845       return CImg<T>().load_exr(filename);
31846     }
31847 
31848     //! Load an image from a PANDORE file.
31849     CImg<T>& load_pandore(const char *const filename) {
31850       return _load_pandore(0,filename);
31851     }
31852 
31853     static CImg<T> get_load_pandore(const char *const filename) {
31854       return CImg<T>().load_pandore(filename);
31855     }
31856 
31857     //! Load an image from a PANDORE file.
31858     CImg<T>& load_pandore(std::FILE *const file) {
31859       return _load_pandore(file,0);
31860     }
31861 
31862     static CImg<T> get_load_pandore(std::FILE *const file) {
31863       return CImg<T>().load_pandore(file);
31864     }
31865 
31866     CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
31867 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
31868         cimg::fread(dims,nbdim,nfile); \
31869         if (endian) cimg::invert_endianness(dims,nbdim); \
31870         assign(nwidth,nheight,ndepth,ndim); \
31871         const unsigned int siz = size(); \
31872         stype *buffer = new stype[siz]; \
31873         cimg::fread(buffer,siz,nfile); \
31874         if (endian) cimg::invert_endianness(buffer,siz); \
31875         T *ptrd = _data; \
31876         cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
31877         buffer-=siz; \
31878         delete[] buffer
31879 
31880 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
31881         if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
31882         else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
31883         else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
31884         else throw CImgIOException(_cimg_instance \
31885                                    "load_pandore() : Unknown pixel datatype in file '%s'.", \
31886                                    cimg_instance, \
31887                                    filename?filename:"(FILE*)"); }
31888 
31889       if (!file && !filename)
31890         throw CImgArgumentException(_cimg_instance
31891                                     "load_pandore() : Specified filename is (null).",
31892                                     cimg_instance);
31893 
31894       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
31895       char header[32] = { 0 };
31896       cimg::fread(header,12,nfile);
31897       if (cimg::strncasecmp("PANDORE",header,7)) {
31898         if (!file) cimg::fclose(nfile);
31899         throw CImgIOException(_cimg_instance
31900                               "load_pandore() : PANDORE header not found in file '%s'.",
31901                               cimg_instance,
31902                               filename?filename:"(FILE*)");
31903       }
31904       unsigned int imageid, dims[8] = { 0 };
31905       cimg::fread(&imageid,1,nfile);
31906       const bool endian = (imageid>255);
31907       if (endian) cimg::invert_endianness(imageid);
31908       cimg::fread(header,20,nfile);
31909 
31910       switch (imageid) {
31911       case 2: _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
31912       case 3: _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
31913       case 4: _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
31914       case 5: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
31915       case 6: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
31916       case 7: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
31917       case 8: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
31918       case 9: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
31919       case 10: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
31920       case 11 : { // Region 1d
31921         cimg::fread(dims,3,nfile);
31922         if (endian) cimg::invert_endianness(dims,3);
31923         assign(dims[1],1,1,1);
31924         const unsigned siz = size();
31925         if (dims[2]<256) {
31926           unsigned char *buffer = new unsigned char[siz];
31927           cimg::fread(buffer,siz,nfile);
31928           T *ptrd = _data;
31929           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
31930           buffer-=siz;
31931           delete[] buffer;
31932         } else {
31933           if (dims[2]<65536) {
31934             unsigned short *buffer = new unsigned short[siz];
31935             cimg::fread(buffer,siz,nfile);
31936             if (endian) cimg::invert_endianness(buffer,siz);
31937             T *ptrd = _data;
31938             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
31939             buffer-=siz;
31940             delete[] buffer;
31941           } else {
31942             unsigned int *buffer = new unsigned int[siz];
31943             cimg::fread(buffer,siz,nfile);
31944             if (endian) cimg::invert_endianness(buffer,siz);
31945             T *ptrd = _data;
31946             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
31947             buffer-=siz;
31948             delete[] buffer;
31949           }
31950         }
31951       }
31952         break;
31953       case 12 : { // Region 2d
31954         cimg::fread(dims,4,nfile);
31955         if (endian) cimg::invert_endianness(dims,4);
31956         assign(dims[2],dims[1],1,1);
31957         const unsigned int siz = size();
31958         if (dims[3]<256) {
31959           unsigned char *buffer = new unsigned char[siz];
31960           cimg::fread(buffer,siz,nfile);
31961           T *ptrd = _data;
31962           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
31963           buffer-=siz;
31964           delete[] buffer;
31965         } else {
31966           if (dims[3]<65536) {
31967             unsigned short *buffer = new unsigned short[siz];
31968             cimg::fread(buffer,siz,nfile);
31969             if (endian) cimg::invert_endianness(buffer,siz);
31970             T *ptrd = _data;
31971             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
31972             buffer-=siz;
31973             delete[] buffer;
31974           } else {
31975             unsigned long *buffer = new unsigned long[siz];
31976             cimg::fread(buffer,siz,nfile);
31977             if (endian) cimg::invert_endianness(buffer,siz);
31978             T *ptrd = _data;
31979             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
31980             buffer-=siz;
31981             delete[] buffer;
31982           }
31983         }
31984       }
31985         break;
31986       case 13 : { // Region 3d
31987         cimg::fread(dims,5,nfile);
31988         if (endian) cimg::invert_endianness(dims,5);
31989         assign(dims[3],dims[2],dims[1],1);
31990         const unsigned int siz = size();
31991         if (dims[4]<256) {
31992           unsigned char *buffer = new unsigned char[siz];
31993           cimg::fread(buffer,siz,nfile);
31994           T *ptrd = _data;
31995           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
31996           buffer-=siz;
31997           delete[] buffer;
31998         } else {
31999           if (dims[4]<65536) {
32000             unsigned short *buffer = new unsigned short[siz];
32001             cimg::fread(buffer,siz,nfile);
32002             if (endian) cimg::invert_endianness(buffer,siz);
32003             T *ptrd = _data;
32004             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
32005             buffer-=siz;
32006             delete[] buffer;
32007           } else {
32008             unsigned int *buffer = new unsigned int[siz];
32009             cimg::fread(buffer,siz,nfile);
32010             if (endian) cimg::invert_endianness(buffer,siz);
32011             T *ptrd = _data;
32012             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
32013             buffer-=siz;
32014             delete[] buffer;
32015           }
32016         }
32017       }
32018         break;
32019       case 16: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
32020       case 17: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
32021       case 18: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
32022       case 19: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
32023       case 20: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
32024       case 21: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
32025       case 22: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
32026       case 23: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4);
32027       case 24: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
32028       case 25: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
32029       case 26: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
32030       case 27: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
32031       case 28: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
32032       case 29: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
32033       case 30: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1); break;
32034       case 31: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
32035       case 32: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4); break;
32036       case 33: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
32037       case 34 : { // Points 1d
32038         int ptbuf[4] = { 0 };
32039         cimg::fread(ptbuf,1,nfile);
32040         if (endian) cimg::invert_endianness(ptbuf,1);
32041         assign(1); (*this)(0) = (T)ptbuf[0];
32042       } break;
32043       case 35 : { // Points 2d
32044         int ptbuf[4] = { 0 };
32045         cimg::fread(ptbuf,2,nfile);
32046         if (endian) cimg::invert_endianness(ptbuf,2);
32047         assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
32048       } break;
32049       case 36 : { // Points 3d
32050         int ptbuf[4] = { 0 };
32051         cimg::fread(ptbuf,3,nfile);
32052         if (endian) cimg::invert_endianness(ptbuf,3);
32053         assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
32054       } break;
32055       default :
32056         if (!file) cimg::fclose(nfile);
32057         throw CImgIOException(_cimg_instance
32058                               "load_pandore() : Unable to load data with ID_type %u in file '%s'.",
32059                               cimg_instance,
32060                               imageid,filename?filename:"(FILE*)");
32061       }
32062       if (!file) cimg::fclose(nfile);
32063       return *this;
32064     }
32065 
32066     //! Load an image from a PAR-REC (Philips) file.
32067     CImg<T>& load_parrec(const char *const filename, const char axis='c', const char align='p') {
32068       CImgList<T> list;
32069       list.load_parrec(filename);
32070       if (list._width==1) return list[0].move_to(*this);
32071       return assign(list.get_append(axis,align));
32072     }
32073 
32074     static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const char align='p') {
32075       return CImg<T>().load_parrec(filename,axis,align);
32076     }
32077 
32078     //! Load an image from a .RAW file.
32079     CImg<T>& load_raw(const char *const filename,
32080                       const unsigned int sizex, const unsigned int sizey=1,
32081                       const unsigned int sizez=1, const unsigned int sizev=1,
32082                       const bool multiplexed=false, const bool invert_endianness=false) {
32083       return _load_raw(0,filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
32084     }
32085 
32086     static CImg<T> get_load_raw(const char *const filename,
32087                                 const unsigned int sizex, const unsigned int sizey=1,
32088                                 const unsigned int sizez=1, const unsigned int sizev=1,
32089                                 const bool multiplexed=false, const bool invert_endianness=false) {
32090       return CImg<T>().load_raw(filename,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
32091     }
32092 
32093     //! Load an image from a .RAW file.
32094     CImg<T>& load_raw(std::FILE *const file,
32095                       const unsigned int sizex, const unsigned int sizey=1,
32096                       const unsigned int sizez=1, const unsigned int sizev=1,
32097                       const bool multiplexed=false, const bool invert_endianness=false) {
32098       return _load_raw(file,0,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
32099     }
32100 
32101     static CImg<T> get_load_raw(std::FILE *const file,
32102                                 const unsigned int sizex, const unsigned int sizey=1,
32103                                 const unsigned int sizez=1, const unsigned int sizev=1,
32104                                 const bool multiplexed=false, const bool invert_endianness=false) {
32105       return CImg<T>().load_raw(file,sizex,sizey,sizez,sizev,multiplexed,invert_endianness);
32106     }
32107 
32108     CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
32109                        const unsigned int sizex, const unsigned int sizey,
32110                        const unsigned int sizez, const unsigned int sizev,
32111                        const bool multiplexed, const bool invert_endianness) {
32112       if (!file && !filename)
32113         throw CImgArgumentException(_cimg_instance
32114                                     "load_raw() : Specified filename is (null).",
32115                                     cimg_instance);
32116 
32117       assign(sizex,sizey,sizez,sizev,0);
32118       const unsigned int siz = size();
32119       if (siz) {
32120         std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
32121         if (!multiplexed) {
32122           cimg::fread(_data,siz,nfile);
32123           if (invert_endianness) cimg::invert_endianness(_data,siz);
32124         }
32125         else {
32126           CImg<T> buf(1,1,1,sizev);
32127           cimg_forXYZ(*this,x,y,z) {
32128             cimg::fread(buf._data,sizev,nfile);
32129             if (invert_endianness) cimg::invert_endianness(buf._data,sizev);
32130             set_vector_at(buf,x,y,z); }
32131         }
32132         if (!file) cimg::fclose(nfile);
32133       }
32134       return *this;
32135     }
32136 
32137     //! Load a video sequence using FFMPEG av's libraries.
32138     CImg<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
32139                          const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
32140                          const char axis='z', const char align='p') {
32141       return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).move_to(*this);
32142     }
32143 
32144     static CImg<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
32145                                    const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
32146                                    const char axis='z', const char align='p') {
32147       return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume).get_append(axis,align);
32148     }
32149 
32150     //! Load an image sequence from a YUV file.
32151     CImg<T>& load_yuv(const char *const filename,
32152                       const unsigned int sizex, const unsigned int sizey=1,
32153                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
32154                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const char align='p') {
32155       return get_load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).move_to(*this);
32156     }
32157 
32158     static CImg<T> get_load_yuv(const char *const filename,
32159                                 const unsigned int sizex, const unsigned int sizey=1,
32160                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
32161                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const char align='p') {
32162       return CImgList<T>().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align);
32163     }
32164 
32165     //! Load an image sequence from a YUV file.
32166     CImg<T>& load_yuv(std::FILE *const file,
32167                       const unsigned int sizex, const unsigned int sizey=1,
32168                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
32169                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const char align='p') {
32170       return get_load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb,axis,align).move_to(*this);
32171     }
32172 
32173     static CImg<T> get_load_yuv(std::FILE *const file,
32174                                 const unsigned int sizex, const unsigned int sizey=1,
32175                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
32176                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z', const char align='p') {
32177       return CImgList<T>().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis,align);
32178     }
32179 
32180     //! Load a 3d object from a .OFF file.
32181     template<typename tf, typename tc>
32182     CImg<T>& load_off(const char *const filename, CImgList<tf>& primitives, CImgList<tc>& colors) {
32183       return _load_off(0,filename,primitives,colors);
32184     }
32185 
32186     template<typename tf, typename tc>
32187     static CImg<T> get_load_off(const char *const filename, CImgList<tf>& primitives, CImgList<tc>& colors) {
32188       return CImg<T>().load_off(filename,primitives,colors);
32189     }
32190 
32191     //! Load a 3d object from a .OFF file.
32192     template<typename tf, typename tc>
32193     CImg<T>& load_off(std::FILE *const file, CImgList<tf>& primitives, CImgList<tc>& colors) {
32194       return _load_off(file,0,primitives,colors);
32195     }
32196 
32197     template<typename tf, typename tc>
32198     static CImg<T> get_load_off(std::FILE *const file, CImgList<tf>& primitives, CImgList<tc>& colors) {
32199       return CImg<T>().load_off(file,primitives,colors);
32200     }
32201 
32202     template<typename tf, typename tc>
32203     CImg<T>& _load_off(std::FILE *const file, const char *const filename,
32204                        CImgList<tf>& primitives, CImgList<tc>& colors) {
32205       if (!file && !filename)
32206         throw CImgArgumentException(_cimg_instance
32207                                     "load_off() : Specified filename is (null).",
32208                                     cimg_instance);
32209 
32210       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
32211       unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
32212       char line[256] = { 0 };
32213       int err;
32214 
32215       // Skip comments, and read magic string OFF
32216       do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#'));
32217       if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
32218         if (!file) cimg::fclose(nfile);
32219         throw CImgIOException(_cimg_instance
32220                               "load_off() : OFF header not found in file '%s'.",
32221                               cimg_instance,
32222                               filename?filename:"(FILE*)");
32223       }
32224       do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#'));
32225       if ((err = std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
32226         if (!file) cimg::fclose(nfile);
32227         throw CImgIOException(_cimg_instance
32228                               "load_off() : Invalid number of vertices or primitives specified in file '%s'.",
32229                               cimg_instance,
32230                               filename?filename:"(FILE*)");
32231       }
32232 
32233       // Read points data
32234       assign(nb_points,3);
32235       float X = 0, Y = 0, Z = 0;
32236       cimg_forX(*this,l) {
32237         do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#'));
32238         if ((err = std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
32239           if (!file) cimg::fclose(nfile);
32240           throw CImgIOException(_cimg_instance
32241                                 "load_off() : Failed to read vertex %u/%u in file '%s'.",
32242                                 cimg_instance,
32243                                 l+1,nb_points,filename?filename:"(FILE*)");
32244         }
32245         (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
32246       }
32247 
32248       // Read primitive data
32249       primitives.assign();
32250       colors.assign();
32251       bool stopflag = false;
32252       while (!stopflag) {
32253         float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
32254         unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
32255         line[0] = 0;
32256         if ((err = std::fscanf(nfile,"%u",&prim))!=1) stopflag=true;
32257         else {
32258           ++nb_read;
32259           switch (prim) {
32260           case 1 : {
32261             if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line))<2) {
32262               cimg::warn(_cimg_instance
32263                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
32264                          cimg_instance,
32265                          nb_read,nb_primitives,filename?filename:"(FILE*)");
32266 
32267               err = std::fscanf(nfile,"%*[^\n] ");
32268             } else {
32269               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
32270               CImg<tf>::vector(i0).move_to(primitives);
32271               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
32272             }
32273           } break;
32274           case 2 : {
32275             if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line))<2) {
32276               cimg::warn(_cimg_instance
32277                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
32278                          cimg_instance,
32279                          nb_read,nb_primitives,filename?filename:"(FILE*)");
32280 
32281               err = std::fscanf(nfile,"%*[^\n] ");
32282             } else {
32283               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
32284               CImg<tf>::vector(i0,i1).move_to(primitives);
32285               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
32286             }
32287           } break;
32288           case 3 : {
32289             if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line))<3) {
32290               cimg::warn(_cimg_instance
32291                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
32292                          cimg_instance,
32293                          nb_read,nb_primitives,filename?filename:"(FILE*)");
32294 
32295               err = std::fscanf(nfile,"%*[^\n] ");
32296             } else {
32297               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
32298               CImg<tf>::vector(i0,i2,i1).move_to(primitives);
32299               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
32300             }
32301           } break;
32302           case 4 : {
32303             if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line))<4) {
32304               cimg::warn(_cimg_instance
32305                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
32306                          cimg_instance,
32307                          nb_read,nb_primitives,filename?filename:"(FILE*)");
32308 
32309               err = std::fscanf(nfile,"%*[^\n] ");
32310             } else {
32311               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
32312               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
32313               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255)).move_to(colors);
32314             }
32315           } break;
32316           case 5 : {
32317             if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line))<5) {
32318               cimg::warn(_cimg_instance
32319                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
32320                          cimg_instance,
32321                          nb_read,nb_primitives,filename?filename:"(FILE*)");
32322 
32323               err = std::fscanf(nfile,"%*[^\n] ");
32324             } else {
32325               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
32326               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
32327               CImg<tf>::vector(i0,i4,i3).move_to(primitives);
32328               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255)));
32329               ++nb_primitives;
32330             }
32331           } break;
32332           case 6 : {
32333             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<6) {
32334               cimg::warn(_cimg_instance
32335                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
32336                          cimg_instance,
32337                          nb_read,nb_primitives,filename?filename:"(FILE*)");
32338 
32339               err = std::fscanf(nfile,"%*[^\n] ");
32340             } else {
32341               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
32342               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
32343               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
32344               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255)));
32345               ++nb_primitives;
32346             }
32347           } break;
32348           case 7 : {
32349             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line))<7) {
32350               cimg::warn(_cimg_instance
32351                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
32352                          cimg_instance,
32353                          nb_read,nb_primitives,filename?filename:"(FILE*)");
32354 
32355               err = std::fscanf(nfile,"%*[^\n] ");
32356             } else {
32357               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
32358               CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
32359               CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
32360               CImg<tf>::vector(i3,i2,i1).move_to(primitives);
32361               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255)));
32362               ++(++nb_primitives);
32363             }
32364           } break;
32365           case 8 : {
32366             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line))<7) {
32367               cimg::warn(_cimg_instance
32368                          "load_off() : Failed to read primitive %u/%u from file '%s'.",
32369                          cimg_instance,
32370                          nb_read,nb_primitives,filename?filename:"(FILE*)");
32371 
32372               err = std::fscanf(nfile,"%*[^\n] ");
32373             } else {
32374               err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
32375               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
32376               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
32377               CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
32378               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255)));
32379               ++(++nb_primitives);
32380             }
32381           } break;
32382           default :
32383             cimg::warn(_cimg_instance
32384                        "load_off() : Failed to read primitive %u/%u (%u vertices) from file '%s'.",
32385                        cimg_instance,
32386                        nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
32387 
32388             err = std::fscanf(nfile,"%*[^\n] ");
32389           }
32390         }
32391       }
32392       if (!file) cimg::fclose(nfile);
32393       if (primitives._width!=nb_primitives)
32394         cimg::warn(_cimg_instance
32395                    "load_off() : Only %u/%u primitives read from file '%s'.",
32396                    cimg_instance,
32397                    primitives._width,nb_primitives,filename?filename:"(FILE*)");
32398 
32399       return *this;
32400     }
32401 
32402     //! Load a video sequence using FFMPEG's external tool 'ffmpeg'.
32403     CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const char align='p') {
32404       return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
32405     }
32406 
32407     static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const char align='p') {
32408       return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
32409     }
32410 
32411     //! Load an image using GraphicsMagick's external tool 'gm'.
32412     CImg<T>& load_graphicsmagick_external(const char *const filename) {
32413       if (!filename)
32414         throw CImgArgumentException(_cimg_instance
32415                                     "load_graphicsmagick_external() : Specified filename is (null).",
32416                                     cimg_instance);
32417 
32418       char command[1024] = { 0 }, filetmp[512] = { 0 };
32419       std::FILE *file = 0;
32420 #if cimg_OS==1
32421       std::sprintf(command,"%s convert \"%s\" pnm:-",cimg::graphicsmagick_path(),filename);
32422       file = popen(command,"r");
32423       if (file) { load_pnm(file); pclose(file); return *this; }
32424 #endif
32425       do {
32426         std::sprintf(filetmp,"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
32427         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
32428       } while (file);
32429       std::sprintf(command,"%s convert \"%s\" \"%s\"",cimg::graphicsmagick_path(),filename,filetmp);
32430       cimg::system(command,cimg::graphicsmagick_path());
32431       if (!(file = std::fopen(filetmp,"rb"))) {
32432         cimg::fclose(cimg::fopen(filename,"r"));
32433         throw CImgIOException(_cimg_instance
32434                               "load_graphicsmagick_external() : Failed to load file '%s' with external command 'gm'.",
32435                               cimg_instance,
32436                               filename);
32437 
32438       } else cimg::fclose(file);
32439       load_pnm(filetmp);
32440       std::remove(filetmp);
32441       return *this;
32442     }
32443 
32444     static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
32445       return CImg<T>().load_graphicsmagick_external(filename);
32446     }
32447 
32448     //! Load a gzipped image file, using external tool 'gunzip'.
32449     CImg<T>& load_gzip_external(const char *const filename) {
32450       if (!filename)
32451         throw CImgIOException(_cimg_instance
32452                               "load_gzip_external() : Specified filename is (null).",
32453                               cimg_instance);
32454 
32455       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
32456       const char
32457         *const ext = cimg::split_filename(filename,body),
32458         *const ext2 = cimg::split_filename(body,0);
32459 
32460       std::FILE *file = 0;
32461       do {
32462         if (!cimg::strcasecmp(ext,"gz")) {
32463           if (*ext2) std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
32464           else std::sprintf(filetmp,"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
32465         } else {
32466           if (*ext) std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
32467           else std::sprintf(filetmp,"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
32468         }
32469         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
32470       } while (file);
32471 
32472       std::sprintf(command,"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp);
32473       cimg::system(command);
32474       if (!(file = std::fopen(filetmp,"rb"))) {
32475         cimg::fclose(cimg::fopen(filename,"r"));
32476         throw CImgIOException(_cimg_instance
32477                               "load_gzip_external() : Failed to load file '%s' with external command 'gunzip'.",
32478                               cimg_instance,
32479                               filename);
32480 
32481       } else cimg::fclose(file);
32482       load(filetmp);
32483       std::remove(filetmp);
32484       return *this;
32485     }
32486 
32487     static CImg<T> get_load_gzip_external(const char *const filename) {
32488       return CImg<T>().load_gzip_external(filename);
32489     }
32490 
32491     //! Load an image using ImageMagick's external tool 'convert'.
32492     CImg<T>& load_imagemagick_external(const char *const filename) {
32493       if (!filename)
32494         throw CImgArgumentException(_cimg_instance
32495                                     "load_imagemagick_external() : Specified filename is (null).",
32496                                     cimg_instance);
32497 
32498       char command[1024] = { 0 }, filetmp[512] = { 0 };
32499       std::FILE *file = 0;
32500 #if cimg_OS==1
32501       std::sprintf(command,"%s \"%s\" pnm:-",cimg::imagemagick_path(),filename);
32502       file = popen(command,"r");
32503       if (file) { load_pnm(file); pclose(file); return *this; }
32504 #endif
32505       do {
32506         std::sprintf(filetmp,"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
32507         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
32508       } while (file);
32509       std::sprintf(command,"%s \"%s\" \"%s\"",cimg::imagemagick_path(),filename,filetmp);
32510       cimg::system(command,cimg::imagemagick_path());
32511       if (!(file = std::fopen(filetmp,"rb"))) {
32512         cimg::fclose(cimg::fopen(filename,"r"));
32513         throw CImgIOException(_cimg_instance
32514                               "load_imagemagick_external() : Failed to load file '%s' with external command 'convert'.",
32515                               cimg_instance,
32516                               filename);
32517 
32518       } else cimg::fclose(file);
32519       load_pnm(filetmp);
32520       std::remove(filetmp);
32521       return *this;
32522     }
32523 
32524     static CImg<T> get_load_imagemagick_external(const char *const filename) {
32525       return CImg<T>().load_imagemagick_external(filename);
32526     }
32527 
32528     //! Load a DICOM image file, using XMedcon's external tool 'medcon'.
32529     CImg<T>& load_medcon_external(const char *const filename) {
32530       if (!filename)
32531         throw CImgArgumentException(_cimg_instance
32532                                     "load_medcon_external() : Specified filename is (null).",
32533                                     cimg_instance);
32534 
32535       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
32536       cimg::fclose(cimg::fopen(filename,"r"));
32537       std::FILE *file = 0;
32538       do {
32539         std::sprintf(filetmp,"%s.hdr",cimg::filenamerand());
32540         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
32541       } while (file);
32542       std::sprintf(command,"%s -w -c anlz -o %s -f %s",cimg::medcon_path(),filetmp,filename);
32543       cimg::system(command);
32544       cimg::split_filename(filetmp,body);
32545       std::sprintf(command,"m000-%s.hdr",body);
32546       file = std::fopen(command,"rb");
32547       if (!file) {
32548         throw CImgIOException(_cimg_instance
32549                               "load_medcon_external() : Failed to load file '%s' with external command 'medcon'.",
32550                               cimg_instance,
32551                               filename);
32552 
32553       } else cimg::fclose(file);
32554       load_analyze(command);
32555       std::remove(command);
32556       std::sprintf(command,"m000-%s.img",body);
32557       std::remove(command);
32558       return *this;
32559     }
32560 
32561     static CImg<T> get_load_medcon_external(const char *const filename) {
32562       return CImg<T>().load_medcon_external(filename);
32563     }
32564 
32565     //! Load a RAW Color Camera image file, using external tool 'dcraw'.
32566     CImg<T>& load_dcraw_external(const char *const filename) {
32567       if (!filename)
32568         throw CImgArgumentException(_cimg_instance
32569                                     "load_dcraw_external() : Specified filename is (null).",
32570                                     cimg_instance);
32571 
32572       char command[1024] = { 0 }, filetmp[512] = { 0 };
32573       std::FILE *file = 0;
32574 #if cimg_OS==1
32575       std::sprintf(command,"%s -w -4 -c \"%s\"",cimg::dcraw_path(),filename);
32576       file = popen(command,"r");
32577       if (file) { load_pnm(file); pclose(file); return *this; }
32578 #endif
32579       do {
32580         std::sprintf(filetmp,"%s%c%s.ppm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
32581         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
32582       } while (file);
32583       std::sprintf(command,"%s -w -4 -c \"%s\" > %s",cimg::dcraw_path(),filename,filetmp);
32584       cimg::system(command,cimg::dcraw_path());
32585       if (!(file = std::fopen(filetmp,"rb"))) {
32586         cimg::fclose(cimg::fopen(filename,"r"));
32587         throw CImgIOException(_cimg_instance
32588                               "load_dcraw_external() : Failed to load file '%s' with external command 'dcraw'.",
32589                               cimg_instance,
32590                               filename);
32591 
32592       } else cimg::fclose(file);
32593       load_pnm(filetmp);
32594       std::remove(filetmp);
32595       return *this;
32596     }
32597 
32598     static CImg<T> get_load_dcraw_external(const char *const filename) {
32599       return CImg<T>().load_dcraw_external(filename);
32600     }
32601 
32602     //! Load an image using ImageMagick's or GraphicsMagick's executables.
32603     CImg<T>& load_other(const char *const filename) {
32604       if (!filename)
32605         throw CImgArgumentException(_cimg_instance
32606                                     "load_other() : Specified filename is (null).",
32607                                     cimg_instance);
32608 
32609       const unsigned int omode = cimg::exception_mode();
32610       cimg::exception_mode() = 0;
32611       try { load_magick(filename); }
32612       catch (CImgException&) {
32613         try { load_imagemagick_external(filename); }
32614         catch (CImgException&) {
32615           try { load_graphicsmagick_external(filename); }
32616           catch (CImgException&) {
32617             assign();
32618           }
32619         }
32620       }
32621       cimg::exception_mode() = omode;
32622       if (is_empty())
32623         throw CImgIOException(_cimg_instance
32624                               "load_other() : Failed to load file '%s'. Format is not natively supported, and no external commands succeeded.",
32625                               cimg_instance,
32626                               filename);
32627       return *this;
32628     }
32629 
32630     static CImg<T> get_load_other(const char *const filename) {
32631       return CImg<T>().load_other(filename);
32632     }
32633 
32634     //@}
32635     //---------------------------
32636     //
32637     //! \name Data Output
32638     //@{
32639     //---------------------------
32640 
32641     //! Display informations about the image on the standard error output.
32642     /**
32643        \param title Name for the considered image (optional).
32644        \param display_stats Compute and display image statistics (optional).
32645     **/
32646     const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
32647       int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
32648       static CImg<doubleT> st;
32649       if (!is_empty() && display_stats) {
32650         st = get_stats();
32651         xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
32652         xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
32653       }
32654       const unsigned int siz = size(), msiz = siz*sizeof(T), siz1 = siz-1;
32655       const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2), width1 = _width-1;
32656       char ntitle[64] = { 0 };
32657       if (!title) std::sprintf(ntitle,"CImg<%s>",pixel_type());
32658 
32659       std::fprintf(cimg::output(),"%s: this = %p, size = (%u,%u,%u,%u) [%u %s], data = (%s*)%p",
32660                    title?title:ntitle,(void*)this,_width,_height,_depth,_spectrum,
32661                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
32662                    mdisp==0?"b":(mdisp==1?"Kb":"Mb"),
32663                    pixel_type(),(void*)begin());
32664       if (_data) std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end()-1),_is_shared?"shared":"non-shared");
32665       else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
32666 
32667       if (!is_empty()) cimg_foroff(*this,off) {
32668         std::fprintf(cimg::output(),cimg::type<T>::format(),cimg::type<T>::format(_data[off]));
32669         if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
32670         if (off==7 && siz>16) { off = siz1-8; if (off!=7) std::fprintf(cimg::output(),"... "); }
32671       }
32672       if (!is_empty() && display_stats)
32673         std::fprintf(cimg::output()," ], min = %g, max = %g, mean = %g, std = %g, coords(min) = (%u,%u,%u,%u), coords(max) = (%u,%u,%u,%u).\n",
32674                      st[0],st[1],st[2],std::sqrt(st[3]),xm,ym,zm,vm,xM,yM,zM,vM);
32675       else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
32676       std::fflush(cimg::output());
32677       return *this;
32678     }
32679 
32680     //! Display an image into a CImgDisplay window.
32681     const CImg<T>& display(CImgDisplay& disp) const {
32682       disp.display(*this);
32683       return *this;
32684     }
32685 
32686     //! Display an image in a window with a title \p title, and wait a '_is_closed' or 'keyboard' event.\n
32687     const CImg<T>& display(CImgDisplay &disp, const bool display_info) const {
32688       return _display(disp,0,display_info);
32689     }
32690 
32691     //! Display an image in a window with a title \p title, and wait a '_is_closed' or 'keyboard' event.\n
32692     const CImg<T>& display(const char *const title=0, const bool display_info=true) const {
32693       CImgDisplay disp;
32694       return _display(disp,title,display_info);
32695     }
32696 
32697     const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info) const {
32698       if (is_empty())
32699         throw CImgInstanceException(_cimg_instance
32700                                     "display() : Empty instance.",
32701                                     cimg_instance);
32702 
32703       unsigned int oldw = 0, oldh = 0, XYZ[3], key = 0;
32704       int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1;
32705       char ntitle[256] = { 0 };
32706       if (!disp) {
32707         if (!title) std::sprintf(ntitle,"CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
32708         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:ntitle,1);
32709       }
32710       std::strncpy(ntitle,disp.title(),255);
32711       if (display_info) print(ntitle);
32712       disp.show().flush();
32713 
32714       CImg<T> zoom;
32715       for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed(); ) {
32716         if (reset_view) {
32717           XYZ[0] = (x0 + x1)/2; XYZ[1] = (y0 + y1)/2; XYZ[2] = (z0 + z1)/2;
32718           x0 = 0; y0 = 0; z0 = 0; x1 = _width - 1; y1 = _height-1; z1 = _depth-1;
32719           oldw = disp.width(); oldh = disp.height();
32720           reset_view = false;
32721         }
32722         if (!x0 && !y0 && !z0 && x1==width()-1 && y1==height()-1 && z1==depth()-1) zoom.assign();
32723         else zoom = get_crop(x0,y0,z0,x1,y1,z1);
32724 
32725         const unsigned int
32726           dx = 1 + x1 - x0, dy = 1 + y1 - y0, dz = 1 + z1 - z0,
32727           tw = dx + (dz>1?dz:0), th = dy + (dz>1?dz:0);
32728         if (resize_disp) {
32729           const unsigned int
32730             ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh,
32731             dM = cimg::max(ttw,tth), diM = (unsigned int)cimg::max(disp.width(),disp.height()),
32732             imgw = cimg::max(16U,ttw*diM/dM), imgh = cimg::max(16U,tth*diM/dM);
32733           disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
32734           resize_disp = false;
32735         }
32736         oldw = tw; oldh = th;
32737 
32738         bool
32739           go_up = false, go_down = false, go_left = false, go_right = false,
32740           go_inc = false, go_dec = false, go_in = false, go_out = false,
32741           go_in_center = false;
32742         const CImg<T>& visu = zoom?zoom:*this;
32743         const CImg<intT> selection = visu._get_select(disp,0,2,XYZ,0,x0,y0,z0);
32744 
32745         if (disp.wheel()) {
32746           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { go_out = !(go_in = disp.wheel()>0); go_in_center = false; }
32747           else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { go_right = !(go_left = disp.wheel()>0); }
32748           else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { go_down = !(go_up = disp.wheel()>0); }
32749           disp.set_wheel();
32750         }
32751 
32752         const int
32753           sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
32754           sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
32755         if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
32756           x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; x0+=sx0; y0+=sy0; z0+=sz0;
32757           if (sx0==sx1 && sy0==sy1 && sz0==sz1) reset_view = true;
32758           resize_disp = true;
32759         } else switch (key = disp.key()) {
32760 #if cimg_OS!=2
32761           case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
32762 #endif
32763           case 0 : case cimg::keyCTRLLEFT : case cimg::keyPAD5 : case cimg::keySHIFTLEFT :
32764 #if cimg_OS!=2
32765           case cimg::keyALTGR :
32766 #endif
32767           case cimg::keyALT : key = 0; break;
32768           case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { // Special mode : play stack of frames
32769           const unsigned int
32770             w1 = visu._width*disp.width()/(visu._width+(visu._depth>1?visu._depth:0)),
32771             h1 = visu._height*disp.height()/(visu._height+(visu._depth>1?visu._depth:0));
32772           float frametiming = 5;
32773           bool is_stopped = false;
32774           disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
32775           for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
32776             if (disp.is_resized()) disp.resize(false);
32777             if (!timer) {
32778               visu.get_slice(XYZ[2]).display(disp.set_title("%s | z=%d",ntitle,XYZ[2]));
32779               (++XYZ[2])%=visu._depth;
32780             }
32781             if (!is_stopped) { if (++timer>(unsigned int)frametiming) timer = 0; } else timer = ~0U;
32782             if (disp.wheel()) { frametiming-=disp.wheel()/3.0f; disp.set_wheel(); }
32783             switch (key = disp.key()) {
32784 #if cimg_OS!=2
32785             case cimg::keyCTRLRIGHT :
32786 #endif
32787             case cimg::keyCTRLLEFT :key = 0; break;
32788             case cimg::keyPAGEUP : frametiming-=0.3f; key = 0; break;
32789             case cimg::keyPAGEDOWN : frametiming+=0.3f; key = 0; break;
32790             case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
32791             case cimg::keyARROWLEFT : case cimg::keyARROWUP :
32792               is_stopped = true; timer = 0; key = 0; break;
32793             case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN :
32794               is_stopped = true; (XYZ[2]+=visu._depth-2)%=visu._depth; timer = 0; key = 0; break;
32795             case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32796                 disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
32797                                                   CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
32798                 disp.set_key(key,false); key = 0;
32799               } break;
32800             case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32801               disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
32802             } break;
32803             case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32804               disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
32805             } break;
32806             case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
32807               disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen().set_key(key,false); key = 0;
32808             } break;
32809             }
32810             frametiming = frametiming<1?1:(frametiming>39?39:frametiming);
32811             disp.wait(20);
32812           }
32813           const unsigned int
32814             w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
32815             h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
32816           disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(ntitle).set_key().set_button().set_wheel();
32817           key = 0;
32818         } break;
32819         case cimg::keyHOME : case cimg::keyBACKSPACE : reset_view = resize_disp = true; key = 0; break;
32820         case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
32821         case cimg::keyPADSUB : go_out = true; key = 0; break;
32822         case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
32823         case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
32824         case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
32825         case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
32826         case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
32827         case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
32828         case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
32829         case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
32830         case cimg::keyPAGEUP : go_inc = true; key = 0; break;
32831         case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
32832         }
32833         if (go_in) {
32834           const int
32835             mx = go_in_center?disp.width()/2:disp.mouse_x(),
32836             my = go_in_center?disp.height()/2:disp.mouse_y(),
32837             mX = mx*(_width+(_depth>1?_depth:0))/disp.width(),
32838             mY = my*(_height+(_depth>1?_depth:0))/disp.height();
32839           int X = XYZ[0], Y = XYZ[1], Z = XYZ[2];
32840           if (mX<width() && mY<height())  { X = x0 + mX*(1+x1-x0)/_width; Y = y0 + mY*(1+y1-y0)/_height; Z = XYZ[2]; }
32841           if (mX<width() && mY>=height()) { X = x0 + mX*(1+x1-x0)/_width; Z = z0 + (mY-_height)*(1+z1-z0)/_depth; Y = XYZ[1]; }
32842           if (mX>=width() && mY<height()) { Y = y0 + mY*(1+y1-y0)/_height; Z = z0 + (mX-_width)*(1+z1-z0)/_depth; X = XYZ[0]; }
32843           if (x1-x0>4) { x0 = X - 7*(X-x0)/8; x1 = X + 7*(x1-X)/8; }
32844           if (y1-y0>4) { y0 = Y - 7*(Y-y0)/8; y1 = Y + 7*(y1-Y)/8; }
32845           if (z1-z0>4) { z0 = Z - 7*(Z-z0)/8; z1 = Z + 7*(z1-Z)/8; }
32846         }
32847         if (go_out) {
32848           const int
32849             deltax = (x1-x0)/8, deltay = (y1-y0)/8, deltaz = (z1-z0)/8,
32850             ndeltax = deltax?deltax:(_width>1?1:0),
32851             ndeltay = deltay?deltay:(_height>1?1:0),
32852             ndeltaz = deltaz?deltaz:(_depth>1?1:0);
32853           x0-=ndeltax; y0-=ndeltay; z0-=ndeltaz;
32854           x1+=ndeltax; y1+=ndeltay; z1+=ndeltaz;
32855           if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
32856           if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
32857           if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
32858           if (x1>=width()) { x0-=(x1-width()+1); x1 = width()-1; if (x0<0) x0 = 0; }
32859           if (y1>=height()) { y0-=(y1-height()+1); y1 = height()-1; if (y0<0) y0 = 0; }
32860           if (z1>=depth()) { z0-=(z1-depth()+1); z1 = depth()-1; if (z0<0) z0 = 0; }
32861         }
32862         if (go_left) {
32863           const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0);
32864           if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
32865           else { x1-=x0; x0 = 0; }
32866         }
32867         if (go_right) {
32868           const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0);
32869           if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
32870           else { x0+=(width()-1-x1); x1 = width()-1; }
32871         }
32872         if (go_up) {
32873           const int delta = (y1-y0)/5, ndelta = delta?delta:(_height>1?1:0);
32874           if (y0-ndelta>=0) { y0-=ndelta; y1-=ndelta; }
32875           else { y1-=y0; y0 = 0; }
32876         }
32877         if (go_down) {
32878           const int delta = (y1-y0)/5, ndelta = delta?delta:(_height>1?1:0);
32879           if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
32880           else { y0+=(height()-1-y1); y1 = height()-1; }
32881         }
32882         if (go_inc) {
32883           const int delta = (z1-z0)/5, ndelta = delta?delta:(_depth>1?1:0);
32884           if (z0-ndelta>=0) { z0-=ndelta; z1-=ndelta; }
32885           else { z1-=z0; z0 = 0; }
32886         }
32887         if (go_dec) {
32888           const int delta = (z1-z0)/5, ndelta = delta?delta:(_depth>1?1:0);
32889           if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
32890           else { z0+=(depth()-1-z1); z1 = depth()-1; }
32891         }
32892       }
32893       disp.set_key(key);
32894       return *this;
32895     }
32896 
32897     //! High-level interface for displaying a 3d object.
32898     template<typename tp, typename tf, typename tc, typename to>
32899     const CImg<T>& display_object3d(CImgDisplay& disp,
32900                                     const CImg<tp>& vertices,
32901                                     const CImgList<tf>& primitives,
32902                                     const CImgList<tc>& colors,
32903                                     const to& opacities,
32904                                     const bool centering=true,
32905                                     const int render_static=4, const int render_motion=1,
32906                                     const bool double_sided=true, const float focale=500,
32907                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
32908                                     const float specular_light=0.2f, const float specular_shine=0.1f,
32909                                     const bool display_axes=true, float *const pose_matrix=0) const {
32910       return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
32911                                render_motion,double_sided,focale,
32912                                light_x,light_y,light_z,specular_light,specular_shine,
32913                                display_axes,pose_matrix);
32914     }
32915 
32916     //! High-level interface for displaying a 3d object.
32917     template<typename tp, typename tf, typename tc, typename to>
32918     const CImg<T>& display_object3d(const char *const title,
32919                                     const CImg<tp>& vertices,
32920                                     const CImgList<tf>& primitives,
32921                                     const CImgList<tc>& colors,
32922                                     const to& opacities,
32923                                     const bool centering=true,
32924                                     const int render_static=4, const int render_motion=1,
32925                                     const bool double_sided=true, const float focale=500,
32926                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
32927                                     const float specular_light=0.2f, const float specular_shine=0.1f,
32928                                     const bool display_axes=true, float *const pose_matrix=0) const {
32929       CImgDisplay disp;
32930       return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
32931                                render_motion,double_sided,focale,
32932                                light_x,light_y,light_z,specular_light,specular_shine,
32933                                display_axes,pose_matrix);
32934     }
32935 
32936     //! High-level interface for displaying a 3d object.
32937     template<typename tp, typename tf, typename tc>
32938     const CImg<T>& display_object3d(CImgDisplay &disp,
32939                                     const CImg<tp>& vertices,
32940                                     const CImgList<tf>& primitives,
32941                                     const CImgList<tc>& colors,
32942                                     const bool centering=true,
32943                                     const int render_static=4, const int render_motion=1,
32944                                     const bool double_sided=true, const float focale=500,
32945                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
32946                                     const float specular_light=0.2f, const float specular_shine=0.1f,
32947                                     const bool display_axes=true, float *const pose_matrix=0) const {
32948       return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
32949                               render_static,render_motion,double_sided,focale,
32950                               light_x,light_y,light_z,specular_light,specular_shine,
32951                               display_axes,pose_matrix);
32952     }
32953 
32954     //! High-level interface for displaying a 3d object.
32955     template<typename tp, typename tf, typename tc>
32956     const CImg<T>& display_object3d(const char *const title,
32957                                     const CImg<tp>& vertices,
32958                                     const CImgList<tf>& primitives,
32959                                     const CImgList<tc>& colors,
32960                                     const bool centering=true,
32961                                     const int render_static=4, const int render_motion=1,
32962                                     const bool double_sided=true, const float focale=500,
32963                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
32964                                     const float specular_light=0.2f, const float specular_shine=0.1f,
32965                                     const bool display_axes=true, float *const pose_matrix=0) const {
32966       return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
32967                               render_static,render_motion,double_sided,focale,
32968                               light_x,light_y,light_z,specular_light,specular_shine,
32969                               display_axes,pose_matrix);
32970     }
32971 
32972     //! High-level interface for displaying a 3d object.
32973     template<typename tp, typename tf>
32974     const CImg<T>& display_object3d(CImgDisplay &disp,
32975                                     const CImg<tp>& vertices,
32976                                     const CImgList<tf>& primitives,
32977                                     const bool centering=true,
32978                                     const int render_static=4, const int render_motion=1,
32979                                     const bool double_sided=true, const float focale=500,
32980                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
32981                                     const float specular_light=0.2f, const float specular_shine=0.1f,
32982                                     const bool display_axes=true, float *const pose_matrix=0) const {
32983       return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
32984                               render_static,render_motion,double_sided,focale,
32985                               light_x,light_y,light_z,specular_light,specular_shine,
32986                               display_axes,pose_matrix);
32987     }
32988 
32989     //! High-level interface for displaying a 3d object.
32990     template<typename tp, typename tf>
32991     const CImg<T>& display_object3d(const char *const title,
32992                                     const CImg<tp>& vertices,
32993                                     const CImgList<tf>& primitives,
32994                                     const bool centering=true,
32995                                     const int render_static=4, const int render_motion=1,
32996                                     const bool double_sided=true, const float focale=500,
32997                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
32998                                     const float specular_light=0.2f, const float specular_shine=0.1f,
32999                                     const bool display_axes=true, float *const pose_matrix=0) const {
33000       return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
33001                               render_static,render_motion,double_sided,focale,
33002                               light_x,light_y,light_z,specular_light,specular_shine,
33003                               display_axes,pose_matrix);
33004     }
33005 
33006     //! High-level interface for displaying a 3d object.
33007     template<typename tp>
33008     const CImg<T>& display_object3d(CImgDisplay &disp,
33009                                     const CImg<tp>& vertices,
33010                                     const bool centering=true,
33011                                     const int render_static=4, const int render_motion=1,
33012                                     const bool double_sided=true, const float focale=500,
33013                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
33014                                     const float specular_light=0.2f, const float specular_shine=0.1f,
33015                                     const bool display_axes=true, float *const pose_matrix=0) const {
33016       return display_object3d(disp,vertices,CImgList<uintT>(),centering,
33017                               render_static,render_motion,double_sided,focale,
33018                               light_x,light_y,light_z,specular_light,specular_shine,
33019                               display_axes,pose_matrix);
33020     }
33021 
33022     //! High-level interface for displaying a 3d object.
33023     template<typename tp>
33024     const CImg<T>& display_object3d(const char *const title,
33025                                     const CImg<tp>& vertices,
33026                                     const bool centering=true,
33027                                     const int render_static=4, const int render_motion=1,
33028                                     const bool double_sided=true, const float focale=500,
33029                                     const float light_x=0, const float light_y=0, const float light_z=-5000,
33030                                     const float specular_light=0.2f, const float specular_shine=0.1f,
33031                                     const bool display_axes=true, float *const pose_matrix=0) const {
33032       return display_object3d(title,vertices,CImgList<uintT>(),centering,
33033                               render_static,render_motion,double_sided,focale,
33034                               light_x,light_y,light_z,specular_light,specular_shine,
33035                               display_axes,pose_matrix);
33036     }
33037 
33038     template<typename tp, typename tf, typename tc, typename to>
33039     const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
33040                                      const CImg<tp>& vertices,
33041                                      const CImgList<tf>& primitives,
33042                                      const CImgList<tc>& colors,
33043                                      const to& opacities,
33044                                      const bool centering,
33045                                      const int render_static, const int render_motion,
33046                                      const bool double_sided, const float focale,
33047                                      const float light_x, const float light_y, const float light_z,
33048                                      const float specular_light, const float specular_shine,
33049                                      const bool display_axes, float *const pose_matrix) const {
33050 
33051       // Check input arguments
33052       if (is_empty()) {
33053         if (disp) return CImg<T>(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0).
33054                     _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
33055                                       render_static,render_motion,double_sided,focale,
33056                                       light_x,light_y,light_z,specular_light,specular_shine,
33057                                       display_axes,pose_matrix);
33058         else return CImg<T>(cimg_fitscreen(640,480,1),1,(colors && colors[0].size()==1)?1:3,0).
33059                _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
33060                                  render_static,render_motion,double_sided,focale,
33061                                  light_x,light_y,light_z,specular_light,specular_shine,
33062                                  display_axes,pose_matrix);
33063       } else { if (disp) disp.resize(*this,false); }
33064       char error_message[1024] = { 0 };
33065       if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
33066         throw CImgArgumentException(_cimg_instance
33067                                     "display_object3d() : Invalid specified 3d object (%u,%u) (%s).",
33068                                     cimg_instance,vertices._width,primitives._width,error_message);
33069       if (vertices._width && !primitives) {
33070         CImgList<tf> nprimitives(vertices._width,1,1,1,1);
33071         cimglist_for(nprimitives,l) nprimitives(l,0) = l;
33072         return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
33073                                  render_static,render_motion,double_sided,focale,
33074                                  light_x,light_y,light_z,specular_light,specular_shine,
33075                                  display_axes,pose_matrix);
33076       }
33077       if (!disp) {
33078         char ntitle[256] = { 0 };
33079         if (!title) { std::sprintf(ntitle,"CImg<%s> (%u vertices, %u primitives)",pixel_type(),vertices._width,primitives._width); }
33080         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:ntitle,3);
33081       }
33082 
33083       // Init 3d objects and compute object statistics
33084       CImgList<tf> reverse_primitives;
33085       CImg<floatT>
33086         pose, rot_mat,
33087         centered_vertices = centering?CImg<floatT>(vertices._width,3):CImg<floatT>(),
33088         rotated_vertices(vertices._width,3),
33089         bbox_vertices, rotated_bbox_vertices,
33090         axes_vertices, rotated_axes_vertices,
33091         bbox_opacities, axes_opacities;
33092       CImgList<uintT> bbox_primitives, axes_primitives;
33093       CImgList<T> bbox_colors, bbox_colors2, axes_colors;
33094       float dx = 0, dy = 0, dz = 0, ratio = 1;
33095 
33096       T minval = (T)0, maxval = (T)255;
33097       if (disp.normalization() && colors) {
33098         minval = colors.min_max(maxval);
33099         if (minval==maxval) { minval = (T)0; maxval = (T)255; }
33100       }
33101       unsigned int ns_width = 0, ns_height = 0;
33102       const float meanval = (float)mean();
33103       bool color_model = true, ndisplay_axes = display_axes;
33104       int _double_sided = (int)double_sided;
33105       if (cimg::abs(meanval-minval)>cimg::abs(meanval-maxval)) color_model = false;
33106       const CImg<T>
33107         background_color(1,1,1,_spectrum,color_model?minval:maxval),
33108         foreground_color(1,1,1,_spectrum,color_model?maxval:minval);
33109 
33110       float xm = cimg::type<float>::max(), xM = cimg::type<float>::min(), ym = xm, yM = xM, zm = xm, zM = xM;
33111       cimg_forX(vertices,i) {
33112         const float x = (float)vertices(i,0), y = (float)vertices(i,1), z = (float)vertices(i,2);
33113         if (x<xm) xm = x;
33114         if (x>xM) xM = x;
33115         if (y<ym) ym = y;
33116         if (y>yM) yM = y;
33117         if (z<zm) zm = z;
33118         if (z>zM) zM = z;
33119       }
33120       const float delta = cimg::max(xM-xm,yM-ym,zM-zm);
33121       rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
33122                                                    0,20,0,0,22,-6,-6,
33123                                                    0,0,20,0,-6,22,-6,
33124                                                    0,0,0,20,0,0,22);
33125       axes_opacities.assign(3,1,1,1,1);
33126       axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
33127       axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
33128 
33129       // Begin user interaction loop
33130       CImg<T> visu0(*this), visu;
33131       CImg<floatT> zbuffer(visu0.width(),visu0.height(),1,1,0);
33132       bool init = true, clicked = false, redraw = true;
33133       unsigned int key = 0;
33134       int
33135         x0 = 0, y0 = 0, x1 = 0, y1 = 0,
33136         nrender_static = render_static,
33137         nrender_motion = render_motion;
33138       disp.show().flush();
33139 
33140       while (!disp.is_closed() && !key) {
33141 
33142         // Init object position and scale if necessary
33143         if (init) {
33144           ratio = delta>0?(2.0f*cimg::min(disp.width(),disp.height())/(3.0f*delta)):0;
33145           dx = 0.5f*(xM + xm); dy = 0.5f*(yM + ym); dz = 0.5f*(zM + zm);
33146           if (centering) {
33147             cimg_forX(centered_vertices,l) {
33148               centered_vertices(l,0) = (float)((vertices(l,0) - dx)*ratio);
33149               centered_vertices(l,1) = (float)((vertices(l,1) - dy)*ratio);
33150               centered_vertices(l,2) = (float)((vertices(l,2) - dz)*ratio);
33151             }
33152           }
33153           rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
33154                                                        xm,xM,xM,xm,xm,xM,xM,xm,
33155                                                        ym,ym,yM,yM,ym,ym,yM,yM,
33156                                                        zm,zm,zm,zm,zM,zM,zM,zM);
33157           bbox_primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 1,2,6,5, 0,4,7,3, 0,1,5,4, 2,3,7,6);
33158           bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
33159           bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
33160           bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
33161 
33162           if (!pose) {
33163             if (pose_matrix) pose = CImg<floatT>(pose_matrix,4,3,1,1,false);
33164             else pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
33165           }
33166           init = false;
33167           redraw = true;
33168         }
33169 
33170         // Rotate and draw 3d object
33171         if (redraw) {
33172           const float
33173             r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
33174             r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
33175             r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
33176           if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) {
33177             if (centering) cimg_forX(centered_vertices,l) {
33178                 const float x = centered_vertices(l,0), y = centered_vertices(l,1), z = centered_vertices(l,2);
33179                 rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30;
33180                 rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31;
33181                 rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32;
33182               } else cimg_forX(vertices,l) {
33183                 const float
33184                   x = (float)vertices(l,0),
33185                   y = (float)vertices(l,1),
33186                   z = (float)vertices(l,2);
33187                 rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30;
33188                 rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31;
33189                 rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32;
33190               }
33191           } else {
33192             if (!centering) cimg_forX(bbox_vertices,l) {
33193                 const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
33194                 rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
33195                 rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
33196                 rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
33197               } else cimg_forX(bbox_vertices,l) {
33198                 const float x = (bbox_vertices(l,0) - dx)*ratio, y = (bbox_vertices(l,1) - dy)*ratio, z = (bbox_vertices(l,2) - dz)*ratio;
33199                 rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
33200                 rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
33201                 rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
33202               }
33203           }
33204 
33205           // Draw object
33206           visu = visu0;
33207           if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
33208             visu.draw_object3d(visu._width/2.0f,visu._height/2.0f,0,rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
33209               draw_object3d(visu._width/2.0f,visu._height/2.0f,0,rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
33210           else visu.draw_object3d(visu._width/2.0f,visu._height/2.0f,0,
33211                                   rotated_vertices,reverse_primitives?reverse_primitives:primitives,
33212                                   colors,opacities,clicked?nrender_motion:nrender_static,_double_sided==1,focale,
33213                                   width()/2.0f+light_x,height()/2.0f+light_y,light_z,specular_light,specular_shine,
33214                                   (!clicked && nrender_static>0)?zbuffer.fill(0):CImg<floatT>::empty());
33215 
33216           // Draw axes
33217           if (ndisplay_axes) {
33218             const float Xaxes = 25, Yaxes = visu._height - 38.0f;
33219             cimg_forX(axes_vertices,l) {
33220               const float x = axes_vertices(l,0), y = axes_vertices(l,1), z = axes_vertices(l,2);
33221               rotated_axes_vertices(l,0) = r00*x + r10*y + r20*z;
33222               rotated_axes_vertices(l,1) = r01*x + r11*y + r21*z;
33223               rotated_axes_vertices(l,2) = r02*x + r12*y + r22*z;
33224             }
33225             axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f;
33226             axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f;
33227             axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f;
33228             visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,axes_colors,axes_opacities,1,false,focale).
33229               draw_text((int)(Xaxes+rotated_axes_vertices(4,0)),
33230                         (int)(Yaxes+rotated_axes_vertices(4,1)),
33231                         "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
33232               draw_text((int)(Xaxes+rotated_axes_vertices(5,0)),
33233                         (int)(Yaxes+rotated_axes_vertices(5,1)),
33234                         "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
33235               draw_text((int)(Xaxes+rotated_axes_vertices(6,0)),
33236                         (int)(Yaxes+rotated_axes_vertices(6,1)),
33237                         "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
33238           }
33239           visu.display(disp);
33240           if (!clicked || nrender_motion==nrender_static) redraw = false;
33241         }
33242 
33243         // Handle user interaction
33244         disp.wait();
33245         if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
33246           redraw = true;
33247           if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
33248           else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
33249           if (disp.button()&1) {
33250             const float
33251               R = 0.45f*cimg::min(disp.width(),disp.height()),
33252               R2 = R*R,
33253               u0 = (float)(x0-disp.width()/2),
33254               v0 = (float)(y0-disp.height()/2),
33255               u1 = (float)(x1-disp.width()/2),
33256               v1 = (float)(y1-disp.height()/2),
33257               n0 = (float)std::sqrt(u0*u0+v0*v0),
33258               n1 = (float)std::sqrt(u1*u1+v1*v1),
33259               nu0 = n0>R?(u0*R/n0):u0,
33260               nv0 = n0>R?(v0*R/n0):v0,
33261               nw0 = (float)std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)),
33262               nu1 = n1>R?(u1*R/n1):u1,
33263               nv1 = n1>R?(v1*R/n1):v1,
33264               nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)),
33265               u = nv0*nw1-nw0*nv1,
33266               v = nw0*nu1-nu0*nw1,
33267               w = nv0*nu1-nu0*nv1,
33268               n = (float)std::sqrt(u*u+v*v+w*w),
33269               alpha = (float)std::asin(n/R2);
33270             rot_mat = CImg<floatT>::rotation_matrix(u,v,w,alpha);
33271             rot_mat*=pose.get_crop(0,0,2,2);
33272             pose.draw_image(rot_mat);
33273             x0=x1; y0=y1;
33274           }
33275           if (disp.button()&2) { pose(3,2)+=(y1-y0); x0 = x1; y0 = y1; }
33276           if (disp.wheel()) { pose(3,2)-=focale*disp.wheel()/10; disp.set_wheel(); }
33277           if (disp.button()&4) { pose(3,0)+=(x1-x0); pose(3,1)+=(y1-y0); x0 = x1; y0 = y1; }
33278           if ((disp.button()&1) && (disp.button()&2)) {
33279             init = true; disp.set_button(); x0 = x1; y0 = y1;
33280             pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
33281           }
33282         } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
33283 
33284         switch (key = disp.key()) {
33285 #if cimg_OS!=2
33286         case cimg::keyCTRLRIGHT :
33287 #endif
33288         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
33289         case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33290             disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
33291                                               CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
33292               _is_resized = true;
33293             disp.set_key(key,false); key = 0;
33294           } break;
33295         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33296             disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
33297             disp.set_key(key,false); key = 0;
33298           } break;
33299         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33300             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
33301             disp.set_key(key,false); key = 0;
33302           } break;
33303         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33304             if (!ns_width || !ns_height ||
33305                 ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
33306               ns_width = disp.screen_width()*3U/4;
33307               ns_height = disp.screen_height()*3U/4;
33308             }
33309             if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
33310             else {
33311               ns_width = (unsigned int)disp.width(); ns_height = disp.height();
33312               disp.resize(disp.screen_width(),disp.screen_height(),false);
33313             }
33314             disp.toggle_fullscreen()._is_resized = true;
33315             disp.set_key(key,false); key = 0;
33316           } break;
33317         case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Switch single/double-sided primitives.
33318             if (--_double_sided==-2) _double_sided = 1;
33319             if (_double_sided>=0) reverse_primitives.assign();
33320             else primitives.get_reverse_object3d().move_to(reverse_primitives);
33321             disp.set_key(key,false); key = 0; redraw = true;
33322           } break;
33323         case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
33324             if (zbuffer) zbuffer.assign();
33325             else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
33326             disp.set_key(key,false); key = 0; redraw = true;
33327           } break;
33328         case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3d axes.
33329             ndisplay_axes = !ndisplay_axes;
33330             disp.set_key(key,false); key = 0; redraw = true;
33331           } break;
33332         case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points.
33333             nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
33334             disp.set_key(key,false); key = 0; redraw = true;
33335           } break;
33336         case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines.
33337             nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
33338             disp.set_key(key,false); key = 0; redraw = true;
33339           } break;
33340         case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat.
33341             nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
33342             disp.set_key(key,false); key = 0; redraw = true;
33343           } break;
33344         case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded.
33345             nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
33346             disp.set_key(key,false); key = 0; redraw = true;
33347           } break;
33348         case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to gouraud-shaded.
33349             nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
33350             disp.set_key(key,false); key = 0; redraw = true;
33351           } break;
33352         case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded.
33353             nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
33354             disp.set_key(key,false); key = 0; redraw = true;
33355           } break;
33356         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
33357             static unsigned int snap_number = 0;
33358             char filename[32] = { 0 };
33359             std::FILE *file;
33360             do {
33361               std::sprintf(filename,"CImg_%.4u.bmp",snap_number++);
33362               if ((file=std::fopen(filename,"r"))!=0) std::fclose(file);
33363             } while (file);
33364             (+visu).draw_text(0,0," Saving snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
33365             visu.save(filename);
33366             visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
33367             disp.set_key(key,false); key = 0;
33368           } break;
33369         case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
33370             static unsigned int snap_number = 0;
33371             char filename[32] = { 0 };
33372             std::FILE *file;
33373             do {
33374               std::sprintf(filename,"CImg_%.4u.off",snap_number++);
33375               if ((file=std::fopen(filename,"r"))!=0) std::fclose(file);
33376             } while (file);
33377             visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp);
33378             vertices.save_off(filename,reverse_primitives?reverse_primitives:primitives,colors);
33379             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
33380             disp.set_key(key,false); key = 0;
33381           } break;
33382         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
33383             static unsigned int snap_number = 0;
33384             char filename[32] = { 0 };
33385             std::FILE *file;
33386             do {
33387               std::sprintf(filename,"CImg_%.4u.cimg",snap_number++);
33388               if ((file=std::fopen(filename,"r"))!=0) std::fclose(file);
33389             } while (file);
33390             visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp);
33391             vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).save_cimg(filename);
33392             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
33393             disp.set_key(key,false); key = 0;
33394           } break;
33395 #ifdef cimg_use_board
33396         case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
33397             static unsigned int snap_number = 0;
33398             char filename[32] = { 0 };
33399             std::FILE *file;
33400             do {
33401               std::sprintf(filename,"CImg_%.4u.eps",snap_number++);
33402               if ((file=std::fopen(filename,"r"))!=0) std::fclose(file);
33403             } while (file);
33404             visu.draw_text(0,0," Saving EPS snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
33405             LibBoard::Board board;
33406             (+visu).draw_object3d(board,visu._width/2.0f,visu._height/2.0f,0,
33407                                   rotated_vertices,reverse_primitives?reverse_primitives:primitives,
33408                                   colors,opacities,clicked?nrender_motion:nrender_static,
33409                                   _double_sided==1,focale,
33410                                   visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_light,specular_shine,
33411                                   zbuffer.fill(0));
33412             board.saveEPS(filename);
33413             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
33414             disp.set_key(key,false); key = 0;
33415           } break;
33416         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
33417             static unsigned int snap_number = 0;
33418             char filename[32] = { 0 };
33419             std::FILE *file;
33420             do {
33421               std::sprintf(filename,"CImg_%.4u.svg",snap_number++);
33422               if ((file=std::fopen(filename,"r"))!=0) std::fclose(file);
33423             } while (file);
33424             visu.draw_text(0,0," Saving SVG snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
33425             LibBoard::Board board;
33426             (+visu).draw_object3d(board,visu._width/2.0f,visu._height/2.0f,0,
33427                                   rotated_vertices,reverse_primitives?reverse_primitives:primitives,
33428                                   colors,opacities,clicked?nrender_motion:nrender_static,
33429                                   _double_sided==1,focale,
33430                                   visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_light,specular_shine,
33431                                   zbuffer.fill(0));
33432             board.saveSVG(filename);
33433             visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
33434             disp.set_key(key,false); key = 0;
33435           } break;
33436 #endif
33437         }
33438         if (disp.is_resized()) { disp.resize(false); visu0 = get_resize(disp,1); if (zbuffer) zbuffer.assign(disp.width(),disp.height()); redraw = true; }
33439       }
33440       if (pose_matrix) std::memcpy(pose_matrix,pose._data,12*sizeof(float));
33441       disp.set_button().set_key(key);
33442       return *this;
33443     }
33444 
33445     //! High-level interface for displaying a graph.
33446     const CImg<T>& display_graph(CImgDisplay &disp,
33447                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
33448                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
33449                                  const char *const labely=0, const double ymin=0, const double ymax=0) const {
33450       if (is_empty())
33451         throw CImgInstanceException(_cimg_instance
33452                                     "display_graph() : Empty instance.",
33453                                     cimg_instance);
33454 
33455       const unsigned int siz = _width*_height*_depth, onormalization = disp.normalization();
33456       if (!disp) { char ntitle[64] = { 0 }; std::sprintf(ntitle,"CImg<%s>",pixel_type()); disp.assign(640,480,ntitle,0); }
33457       disp.show().flush()._normalization = 0;
33458       double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
33459       if (nxmin==nxmax) { nxmin = 0; nxmax = siz; }
33460       int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
33461 
33462       for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed(); ) {
33463         if (reset_view) { x0 = 0; x1 = width()*height()*depth()-1; y0 = ymin; y1 = ymax; reset_view = false; }
33464         CImg<T> zoom(x1-x0+1,1,1,spectrum());
33465         cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1-x0+1,1,1,1,true);
33466 
33467         if (y0==y1) y0 = zoom.min_max(y1);
33468         if (y0==y1) { --y0; ++y1; }
33469         const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
33470                                                            labelx,nxmin + x0*(nxmax-nxmin)/siz,nxmin + (x1+1)*(nxmax-nxmin)/siz,
33471                                                            labely,y0,y1);
33472 
33473         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
33474         if (selection[0]>=0 && selection[2]>=0) {
33475           x1 = x0 + selection[2];
33476           x0+=selection[0];
33477           if (x0==x1) reset_view = true;
33478           if (selection[1]>=0 && selection[3]>=0) {
33479             y0 = y1 - selection[3]*(y1-y0)/(disp.height()-32);
33480             y1-=selection[1]*(y1-y0)/(disp.height()-32);
33481           }
33482         } else {
33483           bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
33484           switch (key = disp.key()) {
33485           case cimg::keyHOME : case cimg::keyBACKSPACE : reset_view = resize_disp = true; key = 0; disp.set_key(); break;
33486           case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
33487           case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
33488           case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key(); break;
33489           case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key(); break;
33490           case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
33491           case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
33492           case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
33493           case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
33494           case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
33495           case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
33496           }
33497           if (disp.wheel()) {
33498             if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_out = !(go_in = disp.wheel()>0);
33499             else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
33500             else go_up = !(go_down = disp.wheel()<0);
33501             key = 0;
33502           }
33503 
33504           if (go_in) {
33505             const int
33506               xsiz = x1 - x0,
33507               mx = (mouse_x-16)*xsiz/(disp.width()-32),
33508               cx = x0 + (mx<0?0:(mx>=xsiz?xsiz:mx));
33509             if (x1-x0>4) {
33510               x0 = cx - 7*(cx-x0)/8; x1 = cx + 7*(x1-cx)/8;
33511               if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33512                 const double
33513                   ysiz = y1 - y0,
33514                   my = (mouse_y-16)*ysiz/(disp.height()-32),
33515                   cy = y1 - (my<0?0:(my>=ysiz?ysiz:my));
33516                 y0 = cy - 7*(cy-y0)/8; y1 = cy + 7*(y1-cy)/8;
33517               } else y0 = y1 = 0;
33518             }
33519           }
33520           if (go_out) {
33521             if (x0>0 || x1<(int)siz-1) {
33522               const int deltax = (x1-x0)/8, ndeltax = deltax?deltax:(siz>1?1:0);
33523               const double ndeltay = (y1-y0)/8;
33524               x0-=ndeltax; x1+=ndeltax;
33525               y0-=ndeltay; y1+=ndeltay;
33526               if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz-1; }
33527               if (x1>=(int)siz) { x0-=(x1-siz+1); x1 = (int)siz-1; if (x0<0) x0 = 0; }
33528             }
33529           }
33530           if (go_left) {
33531             const int delta = (x1-x0)/5, ndelta = delta?delta:1;
33532             if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
33533             else { x1-=x0; x0 = 0; }
33534             go_left = false;
33535           }
33536           if (go_right) {
33537             const int delta = (x1-x0)/5, ndelta = delta?delta:1;
33538             if (x1+ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
33539             else { x0+=(siz-1-x1); x1 = siz-1; }
33540             go_right = false;
33541           }
33542           if (go_up) {
33543             const double delta = (y1-y0)/10, ndelta = delta?delta:1;
33544             y0+=ndelta; y1+=ndelta;
33545             go_up = false;
33546           }
33547           if (go_down) {
33548             const double delta = (y1-y0)/10, ndelta = delta?delta:1;
33549             y0-=ndelta; y1-=ndelta;
33550             go_down = false;
33551           }
33552         }
33553       }
33554       disp._normalization = onormalization;
33555       return *this;
33556     }
33557 
33558     //! High-level interface for displaying a graph.
33559     const CImg<T>& display_graph(const char *const title=0,
33560                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
33561                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
33562                                  const char *const labely=0, const double ymin=0, const double ymax=0) const {
33563       if (is_empty())
33564         throw CImgInstanceException(_cimg_instance
33565                                     "display_graph() : Empty instance.",
33566                                     cimg_instance);
33567 
33568       char ntitle[64] = { 0 }; if (!title) std::sprintf(ntitle,"CImg<%s>",pixel_type());
33569       CImgDisplay disp(cimg_fitscreen(640,480,1),title?title:ntitle,0);
33570       return display_graph(disp,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax);
33571     }
33572 
33573     //! Save the image as a file.
33574     /**
33575        The used file format is defined by the file extension in the filename \p filename.
33576        Parameter \p number can be used to add a 6-digit number to the filename before saving.
33577     **/
33578     const CImg<T>& save(const char *const filename, const int number=-1) const {
33579       if (!filename)
33580         throw CImgArgumentException(_cimg_instance
33581                                     "save() : Specified filename is (null).",
33582                                     cimg_instance);
33583       // Do not test for empty instances, since .cimg format is able to manage empty instances.
33584       const char *const ext = cimg::split_filename(filename);
33585       char nfilename[1024] = { 0 };
33586       const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename;
33587 #ifdef cimg_save_plugin
33588       cimg_save_plugin(fn);
33589 #endif
33590 #ifdef cimg_save_plugin1
33591       cimg_save_plugin1(fn);
33592 #endif
33593 #ifdef cimg_save_plugin2
33594       cimg_save_plugin2(fn);
33595 #endif
33596 #ifdef cimg_save_plugin3
33597       cimg_save_plugin3(fn);
33598 #endif
33599 #ifdef cimg_save_plugin4
33600       cimg_save_plugin4(fn);
33601 #endif
33602 #ifdef cimg_save_plugin5
33603       cimg_save_plugin5(fn);
33604 #endif
33605 #ifdef cimg_save_plugin6
33606       cimg_save_plugin6(fn);
33607 #endif
33608 #ifdef cimg_save_plugin7
33609       cimg_save_plugin7(fn);
33610 #endif
33611 #ifdef cimg_save_plugin8
33612       cimg_save_plugin8(fn);
33613 #endif
33614       // ASCII formats
33615       if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
33616       else if (!cimg::strcasecmp(ext,"dlm") ||
33617                !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
33618       else if (!cimg::strcasecmp(ext,"cpp") ||
33619                !cimg::strcasecmp(ext,"hpp") ||
33620                !cimg::strcasecmp(ext,"h") ||
33621                !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
33622 
33623       // 2d binary formats
33624       else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
33625       else if (!cimg::strcasecmp(ext,"jpg") ||
33626                !cimg::strcasecmp(ext,"jpeg") ||
33627                !cimg::strcasecmp(ext,"jpe") ||
33628                !cimg::strcasecmp(ext,"jfif") ||
33629                !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
33630       else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
33631       else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
33632       else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
33633       else if (!cimg::strcasecmp(ext,"pgm") ||
33634                !cimg::strcasecmp(ext,"ppm") ||
33635                !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
33636       else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
33637       else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
33638       else if (!cimg::strcasecmp(ext,"tif") ||
33639                !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
33640 
33641       // 3d binary formats
33642       else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
33643       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
33644       else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
33645       else if (!cimg::strcasecmp(ext,"hdr") ||
33646                !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
33647       else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
33648       else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
33649       else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
33650 
33651       // Archive files
33652       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
33653 
33654       // Image sequences
33655       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
33656       else if (!cimg::strcasecmp(ext,"avi") ||
33657                !cimg::strcasecmp(ext,"mov") ||
33658                !cimg::strcasecmp(ext,"asf") ||
33659                !cimg::strcasecmp(ext,"divx") ||
33660                !cimg::strcasecmp(ext,"flv") ||
33661                !cimg::strcasecmp(ext,"mpg") ||
33662                !cimg::strcasecmp(ext,"m1v") ||
33663                !cimg::strcasecmp(ext,"m2v") ||
33664                !cimg::strcasecmp(ext,"m4v") ||
33665                !cimg::strcasecmp(ext,"mjp") ||
33666                !cimg::strcasecmp(ext,"mkv") ||
33667                !cimg::strcasecmp(ext,"mpe") ||
33668                !cimg::strcasecmp(ext,"movie") ||
33669                !cimg::strcasecmp(ext,"ogm") ||
33670                !cimg::strcasecmp(ext,"qt") ||
33671                !cimg::strcasecmp(ext,"rm") ||
33672                !cimg::strcasecmp(ext,"vob") ||
33673                !cimg::strcasecmp(ext,"wmv") ||
33674                !cimg::strcasecmp(ext,"xvid") ||
33675                !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
33676       return save_other(fn);
33677     }
33678 
33679     // Save the image as an ASCII file (ASCII Raw + simple header) (internal).
33680     const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
33681       if (!file && !filename)
33682         throw CImgArgumentException(_cimg_instance
33683                                     "save_ascii() : Specified filename is (null).",
33684                                     cimg_instance);
33685       if (is_empty())
33686         throw CImgInstanceException(_cimg_instance
33687                                     "save_ascii() : Empty instance, for file '%s'.",
33688                                     cimg_instance,
33689                                     filename?filename:"(FILE*)");
33690 
33691       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
33692       std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
33693       const T* ptrs = _data;
33694       cimg_forYZC(*this,y,z,c) {
33695         cimg_forX(*this,x) std::fprintf(nfile,"%g ",(double)*(ptrs++));
33696         std::fputc('\n',nfile);
33697       }
33698       if (!file) cimg::fclose(nfile);
33699       return *this;
33700     }
33701 
33702     //! Save the image as an ASCII file (ASCII Raw + simple header).
33703     const CImg<T>& save_ascii(const char *const filename) const {
33704       return _save_ascii(0,filename);
33705     }
33706 
33707     //! Save the image as an ASCII file (ASCII Raw + simple header).
33708     const CImg<T>& save_ascii(std::FILE *const file) const {
33709       return _save_ascii(file,0);
33710     }
33711 
33712     // Save the image as a C or CPP source file (internal).
33713     const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
33714       if (!file && !filename)
33715         throw CImgArgumentException(_cimg_instance
33716                                     "save_cpp() : Specified filename is (null).",
33717                                     cimg_instance);
33718       if (is_empty())
33719         throw CImgInstanceException(_cimg_instance
33720                                     "save_cpp() : Empty instance, for file '%s'.",
33721                                     cimg_instance,
33722                                     filename?filename:"(FILE*)");
33723 
33724       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
33725       char varname[1024] = { 0 };
33726       if (filename) std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname);
33727       if (!*varname) std::sprintf(varname,"unnamed");
33728       std::fprintf(nfile,
33729                    "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
33730                    "%s data_%s[] = { \n  ",
33731                    varname,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname);
33732       for (unsigned int off = 0, siz = size()-1; off<=siz; ++off) {
33733         std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
33734         if (off==siz) std::fprintf(nfile," };\n");
33735         else if (!((off+1)%16)) std::fprintf(nfile,",\n  ");
33736         else std::fprintf(nfile,", ");
33737       }
33738       if (!file) cimg::fclose(nfile);
33739       return *this;
33740     }
33741 
33742     //! Save the image as a CPP source file.
33743     const CImg<T>& save_cpp(const char *const filename) const {
33744       return _save_cpp(0,filename);
33745     }
33746 
33747     //! Save the image as a CPP source file.
33748     const CImg<T>& save_cpp(std::FILE *const file) const {
33749       return _save_cpp(file,0);
33750     }
33751 
33752     // Save the image as a DLM file (internal).
33753     const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
33754       if (!file && !filename)
33755         throw CImgArgumentException(_cimg_instance
33756                                     "save_dlm() : Specified filename is (null).",
33757                                     cimg_instance);
33758       if (is_empty())
33759         throw CImgInstanceException(_cimg_instance
33760                                     "save_dlm() : Empty instance, for file '%s'.",
33761                                     cimg_instance,
33762                                     filename?filename:"(FILE*)");
33763       if (_depth>1)
33764         cimg::warn(_cimg_instance
33765                    "save_dlm() : Instance is volumetric, values along Z will be unrolled in file '%s'.",
33766                    cimg_instance,
33767                    filename?filename:"(FILE*)");
33768 
33769       if (_spectrum>1)
33770         cimg::warn(_cimg_instance
33771                    "save_dlm() : Instance is multispectral, values along C will be unrolled in file '%s'.",
33772                    cimg_instance,
33773                    filename?filename:"(FILE*)");
33774 
33775       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
33776       const T* ptrs = _data;
33777       cimg_forYZC(*this,y,z,c) {
33778         cimg_forX(*this,x) std::fprintf(nfile,"%g%s",(double)*(ptrs++),(x==width()-1)?"":",");
33779         std::fputc('\n',nfile);
33780       }
33781       if (!file) cimg::fclose(nfile);
33782       return *this;
33783     }
33784 
33785     //! Save the image as a DLM file.
33786     const CImg<T>& save_dlm(const char *const filename) const {
33787       return _save_dlm(0,filename);
33788     }
33789 
33790     //! Save the image as a DLM file.
33791     const CImg<T>& save_dlm(std::FILE *const file) const {
33792       return _save_dlm(file,0);
33793     }
33794 
33795    // Save the image as a BMP file (internal).
33796     const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
33797       if (!file && !filename)
33798         throw CImgArgumentException(_cimg_instance
33799                                     "save_bmp() : Specified filename is (null).",
33800                                     cimg_instance);
33801       if (is_empty())
33802         throw CImgInstanceException(_cimg_instance
33803                                     "save_bmp() : Empty instance, for file '%s'.",
33804                                     cimg_instance,
33805                                     filename?filename:"(FILE*)");
33806       if (_depth>1)
33807         cimg::warn(_cimg_instance
33808                    "save_bmp() : Instance is volumetric, only the first slice will be saved in file '%s'.",
33809                    cimg_instance,
33810                    filename?filename:"(FILE*)");
33811 
33812       if (_spectrum>3)
33813         cimg::warn(_cimg_instance
33814                    "save_bmp() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
33815                    cimg_instance,
33816                    filename?filename:"(FILE*)");
33817 
33818       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
33819       unsigned char header[54] = { 0 }, align_buf[4] = { 0 };
33820       const unsigned int
33821         align = (4 - (3*_width)%4)%4,
33822         buf_size = (3*_width+align)*height(),
33823         file_size = 54 + buf_size;
33824       header[0] = 'B'; header[1] = 'M';
33825       header[0x02] = file_size&0xFF;
33826       header[0x03] = (file_size>>8)&0xFF;
33827       header[0x04] = (file_size>>16)&0xFF;
33828       header[0x05] = (file_size>>24)&0xFF;
33829       header[0x0A] = 0x36;
33830       header[0x0E] = 0x28;
33831       header[0x12] = _width&0xFF;
33832       header[0x13] = (_width>>8)&0xFF;
33833       header[0x14] = (_width>>16)&0xFF;
33834       header[0x15] = (_width>>24)&0xFF;
33835       header[0x16] = _height&0xFF;
33836       header[0x17] = (_height>>8)&0xFF;
33837       header[0x18] = (_height>>16)&0xFF;
33838       header[0x19] = (_height>>24)&0xFF;
33839       header[0x1A] = 1;
33840       header[0x1B] = 0;
33841       header[0x1C] = 24;
33842       header[0x1D] = 0;
33843       header[0x22] = buf_size&0xFF;
33844       header[0x23] = (buf_size>>8)&0xFF;
33845       header[0x24] = (buf_size>>16)&0xFF;
33846       header[0x25] = (buf_size>>24)&0xFF;
33847       header[0x27] = 0x1;
33848       header[0x2B] = 0x1;
33849       cimg::fwrite(header,54,nfile);
33850 
33851       const T
33852         *ptr_r = data(0,_height-1,0,0),
33853         *ptr_g = (_spectrum>=2)?data(0,_height-1,0,1):0,
33854         *ptr_b = (_spectrum>=3)?data(0,_height-1,0,2):0;
33855 
33856       switch (_spectrum) {
33857       case 1 : {
33858         cimg_forY(*this,y) {
33859           cimg_forX(*this,x) {
33860             const unsigned char val = (unsigned char)*(ptr_r++);
33861             std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
33862           }
33863           cimg::fwrite(align_buf,align,nfile);
33864           ptr_r-=2*_width;
33865         }
33866       } break;
33867       case 2 : {
33868         cimg_forY(*this,y) {
33869           cimg_forX(*this,x) {
33870             std::fputc(0,nfile);
33871             std::fputc((unsigned char)(*(ptr_g++)),nfile);
33872             std::fputc((unsigned char)(*(ptr_r++)),nfile);
33873           }
33874           cimg::fwrite(align_buf,align,nfile);
33875           ptr_r-=2*_width; ptr_g-=2*_width;
33876         }
33877       } break;
33878       default : {
33879         cimg_forY(*this,y) {
33880           cimg_forX(*this,x) {
33881             std::fputc((unsigned char)(*(ptr_b++)),nfile);
33882             std::fputc((unsigned char)(*(ptr_g++)),nfile);
33883             std::fputc((unsigned char)(*(ptr_r++)),nfile);
33884           }
33885           cimg::fwrite(align_buf,align,nfile);
33886           ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
33887         }
33888       }
33889       }
33890       if (!file) cimg::fclose(nfile);
33891       return *this;
33892     }
33893 
33894     //! Save the image as a BMP file.
33895     const CImg<T>& save_bmp(const char *const filename) const {
33896       return _save_bmp(0,filename);
33897     }
33898 
33899     //! Save the image as a BMP file.
33900     const CImg<T>& save_bmp(std::FILE *const file) const {
33901       return _save_bmp(file,0);
33902     }
33903 
33904     // Save a file in JPEG format (internal).
33905     const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
33906       if (!file && !filename)
33907         throw CImgArgumentException(_cimg_instance
33908                                     "save_jpeg() : Specified filename is (null).",
33909                                     cimg_instance);
33910       if (is_empty())
33911         throw CImgInstanceException(_cimg_instance
33912                                     "save_jpeg() : Empty instance, for file '%s'.",
33913                                     cimg_instance,
33914                                     filename?filename:"(FILE*)");
33915       if (_depth>1)
33916         cimg::warn(_cimg_instance
33917                    "save_jpeg() : Instance is volumetric, only the first slice will be saved in file '%s'.",
33918                    cimg_instance,
33919                    filename?filename:"(FILE*)");
33920 
33921 #ifndef cimg_use_jpeg
33922       if (!file) return save_other(filename,quality);
33923       else throw CImgIOException(_cimg_instance
33924                                  "save_jpeg() : Unable to save data in '(*FILE)' unless libjpeg is enabled.",
33925                                  cimg_instance);
33926 #else
33927       unsigned int dimbuf = 0;
33928       J_COLOR_SPACE colortype = JCS_RGB;
33929 
33930       switch(_spectrum) {
33931       case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
33932       case 2 : dimbuf = 3; colortype = JCS_RGB; break;
33933       case 3 : dimbuf = 3; colortype = JCS_RGB; break;
33934       default: dimbuf = 4; colortype = JCS_CMYK; break;
33935       }
33936 
33937       // Call libjpeg functions
33938       struct jpeg_compress_struct cinfo;
33939       struct jpeg_error_mgr jerr;
33940       cinfo.err = jpeg_std_error(&jerr);
33941       jpeg_create_compress(&cinfo);
33942       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
33943       jpeg_stdio_dest(&cinfo,nfile);
33944       cinfo.image_width = _width;
33945       cinfo.image_height = _height;
33946       cinfo.input_components = dimbuf;
33947       cinfo.in_color_space = colortype;
33948       jpeg_set_defaults(&cinfo);
33949       jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
33950       jpeg_start_compress(&cinfo,TRUE);
33951 
33952       JSAMPROW row_pointer[1];
33953       CImg<ucharT> buffer(_width*dimbuf);
33954 
33955       while (cinfo.next_scanline < cinfo.image_height) {
33956         unsigned char *ptrd = buffer._data;
33957 
33958         // Fill pixel buffer
33959         switch (_spectrum) {
33960         case 1 : { // Greyscale images
33961           const T *ptr_g = data(0, cinfo.next_scanline);
33962           for(unsigned int b = 0; b < cinfo.image_width; b++)
33963             *(ptrd++) = (unsigned char)*(ptr_g++);
33964         } break;
33965         case 2 : { // RG images
33966           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
33967             *ptr_g = data(0,cinfo.next_scanline,0,1);
33968           for(unsigned int b = 0; b < cinfo.image_width; b++) {
33969             *(ptrd++) = (unsigned char)*(ptr_r++);
33970             *(ptrd++) = (unsigned char)*(ptr_g++);
33971             *(ptrd++) = 0;
33972           }
33973         } break;
33974         case 3 : { // RGB images
33975           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
33976             *ptr_g = data(0,cinfo.next_scanline,0,1),
33977             *ptr_b = data(0,cinfo.next_scanline,0,2);
33978           for(unsigned int b = 0; b < cinfo.image_width; b++) {
33979             *(ptrd++) = (unsigned char)*(ptr_r++);
33980             *(ptrd++) = (unsigned char)*(ptr_g++);
33981             *(ptrd++) = (unsigned char)*(ptr_b++);
33982           }
33983         } break;
33984         default : { // CMYK images
33985           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
33986             *ptr_g = data(0,cinfo.next_scanline,0,1),
33987             *ptr_b = data(0,cinfo.next_scanline,0,2),
33988             *ptr_a = data(0,cinfo.next_scanline,0,3);
33989           for(unsigned int b = 0; b < cinfo.image_width; b++) {
33990             *(ptrd++) = (unsigned char)*(ptr_r++);
33991             *(ptrd++) = (unsigned char)*(ptr_g++);
33992             *(ptrd++) = (unsigned char)*(ptr_b++);
33993             *(ptrd++) = (unsigned char)*(ptr_a++);
33994           }
33995         }
33996         }
33997         row_pointer[0] = buffer._data;
33998         jpeg_write_scanlines(&cinfo,row_pointer,1);
33999       }
34000       jpeg_finish_compress(&cinfo);
34001       if (!file) cimg::fclose(nfile);
34002       jpeg_destroy_compress(&cinfo);
34003       return *this;
34004 #endif
34005     }
34006 
34007     //! Save a file in JPEG format.
34008     const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
34009       return _save_jpeg(0,filename,quality);
34010     }
34011 
34012     //! Save a file in JPEG format.
34013     const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
34014       return _save_jpeg(file,0,quality);
34015     }
34016 
34017     //! Save the image using built-in ImageMagick++ library.
34018     const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
34019       if (!filename)
34020         throw CImgArgumentException(_cimg_instance
34021                                     "save_magick() : Specified filename is (null).",
34022                                     cimg_instance);
34023       if (is_empty())
34024         throw CImgInstanceException(_cimg_instance
34025                                     "save_magick() : Empty instance, for file '%s'.",
34026                                     cimg_instance,
34027                                     filename);
34028 
34029       unsigned int foo = bytes_per_pixel; foo = 0;
34030 #ifdef cimg_use_magick
34031       double stmin, stmax = (double)max_min(stmin);
34032       if (_depth>1)
34033         cimg::warn(_cimg_instance
34034                    "save_magick() : Instance is volumetric, only the first slice will be saved in file '%s'.",
34035                    cimg_instance,
34036                    filename);
34037 
34038       if (_spectrum>3)
34039         cimg::warn(_cimg_instance
34040                    "save_magick() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
34041                    cimg_instance,
34042                    filename);
34043 
34044       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
34045         cimg::warn(_cimg_instance
34046                    "save_magick() : Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
34047                    cimg_instance,
34048                    filename,stmin,stmax);
34049 
34050       Magick::Image image(Magick::Geometry(_width,_height),"black");
34051       image.type(Magick::TrueColorType);
34052       image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
34053       const T
34054         *ptr_r = data(0,0,0,0),
34055         *ptr_g = _spectrum>1?data(0,0,0,1):0,
34056         *ptr_b = _spectrum>2?data(0,0,0,2):0;
34057       Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
34058       switch (_spectrum) {
34059       case 1 : // Scalar images
34060         for (unsigned int off = _width*_height; off; --off) {
34061           pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
34062           ++pixels;
34063         }
34064         break;
34065       case 2 : // RG images
34066         for (unsigned int off = _width*_height; off; --off) {
34067           pixels->red = (Magick::Quantum)*(ptr_r++);
34068           pixels->green = (Magick::Quantum)*(ptr_g++);
34069           pixels->blue = 0; ++pixels;
34070         }
34071         break;
34072       default : // RGB images
34073         for (unsigned int off = _width*_height; off; --off) {
34074           pixels->red = (Magick::Quantum)*(ptr_r++);
34075           pixels->green = (Magick::Quantum)*(ptr_g++);
34076           pixels->blue = (Magick::Quantum)*(ptr_b++);
34077           ++pixels;
34078         }
34079       }
34080       image.syncPixels();
34081       image.write(filename);
34082 #else
34083       throw CImgIOException(_cimg_instance
34084                             "save_magick() : Unable to save file '%s' unless libMagick++ is enabled.",
34085                             cimg_instance,
34086                             filename);
34087 #endif
34088       return *this;
34089     }
34090 
34091     // Save an image to a PNG file (internal).
34092     // Most of this function has been written by Eric Fausett
34093     const CImg<T>& _save_png(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const {
34094       if (!filename)
34095         throw CImgArgumentException(_cimg_instance
34096                                     "save_png() : Specified filename is (null).",
34097                                     cimg_instance);
34098       if (is_empty())
34099         throw CImgInstanceException(_cimg_instance
34100                                     "save_png() : Empty image, for file '%s'.",
34101                                     cimg_instance,
34102                                     filename?filename:"(FILE*)");
34103 
34104       unsigned int foo = bytes_per_pixel; foo = 0;
34105 #ifndef cimg_use_png
34106       if (!file) return save_other(filename);
34107       else throw CImgIOException(_cimg_instance
34108                                  "save_png() : Unable to save data in '(*FILE)' unless libpng is enabled.",
34109                                  cimg_instance);
34110 #else
34111       const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
34112       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
34113 
34114       double stmin, stmax = (double)max_min(stmin);
34115       if (_depth>1)
34116         cimg::warn(_cimg_instance
34117                    "save_magick() : Instance is volumetric, only the first slice will be saved in file '%s'.",
34118                    cimg_instance,
34119                    filename);
34120 
34121       if (_spectrum>3)
34122         cimg::warn(_cimg_instance
34123                    "save_magick() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
34124                    cimg_instance,
34125                    filename);
34126 
34127       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
34128         cimg::warn(_cimg_instance
34129                    "save_magick() : Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
34130                    cimg_instance,
34131                    filename,stmin,stmax);
34132 
34133       // Setup PNG structures for write
34134       png_voidp user_error_ptr = 0;
34135       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
34136       png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, user_warning_fn);
34137       if(!png_ptr){
34138         if (!file) cimg::fclose(nfile);
34139         throw CImgIOException(_cimg_instance
34140                               "save_png() : Failed to initialize 'png_ptr' structure when saving file '%s'.",
34141                               cimg_instance,
34142                               nfilename?nfilename:"(FILE*)");
34143       }
34144       png_infop info_ptr = png_create_info_struct(png_ptr);
34145       if (!info_ptr) {
34146         png_destroy_write_struct(&png_ptr,(png_infopp)0);
34147         if (!file) cimg::fclose(nfile);
34148         throw CImgIOException(_cimg_instance
34149                               "save_png() : Failed to initialize 'info_ptr' structure when saving file '%s'.",
34150                               cimg_instance,
34151                               nfilename?nfilename:"(FILE*)");
34152       }
34153       if (setjmp(png_jmpbuf(png_ptr))) {
34154         png_destroy_write_struct(&png_ptr, &info_ptr);
34155         if (!file) cimg::fclose(nfile);
34156         throw CImgIOException(_cimg_instance
34157                               "save_png() : Encountered unknown fatal error in libpng when saving file '%s'.",
34158                               cimg_instance,
34159                               nfilename?nfilename:"(FILE*)");
34160       }
34161       png_init_io(png_ptr, nfile);
34162       const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
34163       int color_type;
34164       switch (spectrum()) {
34165       case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
34166       case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
34167       case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
34168       default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
34169       }
34170       const int interlace_type = PNG_INTERLACE_NONE;
34171       const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
34172       const int filter_method = PNG_FILTER_TYPE_DEFAULT;
34173       png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
34174       png_write_info(png_ptr,info_ptr);
34175       const int byte_depth = bit_depth>>3;
34176       const int numChan = spectrum()>4?4:spectrum();
34177       const int pixel_bit_depth_flag = numChan * (bit_depth-1);
34178 
34179       // Allocate Memory for Image Save and Fill pixel data
34180       png_bytep *const imgData = new png_byte*[_height];
34181       for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
34182       const T *pC0 = data(0,0,0,0);
34183       switch (pixel_bit_depth_flag) {
34184       case 7 :  { // Gray 8-bit
34185         cimg_forY(*this,y) {
34186           unsigned char *ptrd = imgData[y];
34187           cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
34188         }
34189       } break;
34190       case 14 : { // Gray w/ Alpha 8-bit
34191         const T *pC1 = data(0,0,0,1);
34192         cimg_forY(*this,y) {
34193           unsigned char *ptrd = imgData[y];
34194           cimg_forX(*this,x) {
34195             *(ptrd++) = (unsigned char)*(pC0++);
34196             *(ptrd++) = (unsigned char)*(pC1++);
34197           }
34198         }
34199       } break;
34200       case 21 :  { // RGB 8-bit
34201         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
34202         cimg_forY(*this,y) {
34203           unsigned char *ptrd = imgData[y];
34204           cimg_forX(*this,x) {
34205             *(ptrd++) = (unsigned char)*(pC0++);
34206             *(ptrd++) = (unsigned char)*(pC1++);
34207             *(ptrd++) = (unsigned char)*(pC2++);
34208           }
34209         }
34210       } break;
34211       case 28 : { // RGB x/ Alpha 8-bit
34212         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
34213         cimg_forY(*this,y){
34214           unsigned char *ptrd = imgData[y];
34215           cimg_forX(*this,x){
34216             *(ptrd++) = (unsigned char)*(pC0++);
34217             *(ptrd++) = (unsigned char)*(pC1++);
34218             *(ptrd++) = (unsigned char)*(pC2++);
34219             *(ptrd++) = (unsigned char)*(pC3++);
34220           }
34221         }
34222       } break;
34223       case 15 : { // Gray 16-bit
34224         cimg_forY(*this,y){
34225           unsigned short *ptrd = (unsigned short*)(imgData[y]);
34226           cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
34227           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
34228         }
34229       } break;
34230       case 30 : { // Gray w/ Alpha 16-bit
34231         const T *pC1 = data(0,0,0,1);
34232         cimg_forY(*this,y){
34233           unsigned short *ptrd = (unsigned short*)(imgData[y]);
34234           cimg_forX(*this,x) {
34235             *(ptrd++) = (unsigned short)*(pC0++);
34236             *(ptrd++) = (unsigned short)*(pC1++);
34237           }
34238           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
34239         }
34240       } break;
34241       case 45 : { // RGB 16-bit
34242         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
34243         cimg_forY(*this,y) {
34244           unsigned short *ptrd = (unsigned short*)(imgData[y]);
34245           cimg_forX(*this,x) {
34246             *(ptrd++) = (unsigned short)*(pC0++);
34247             *(ptrd++) = (unsigned short)*(pC1++);
34248             *(ptrd++) = (unsigned short)*(pC2++);
34249           }
34250           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
34251         }
34252       } break;
34253       case 60 : { // RGB w/ Alpha 16-bit
34254         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
34255         cimg_forY(*this,y) {
34256           unsigned short *ptrd = (unsigned short*)(imgData[y]);
34257           cimg_forX(*this,x) {
34258             *(ptrd++) = (unsigned short)*(pC0++);
34259             *(ptrd++) = (unsigned short)*(pC1++);
34260             *(ptrd++) = (unsigned short)*(pC2++);
34261             *(ptrd++) = (unsigned short)*(pC3++);
34262           }
34263           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
34264         }
34265       } break;
34266       default :
34267         if (!file) cimg::fclose(nfile);
34268         throw CImgIOException(_cimg_instance
34269                               "save_png() : Encountered unknown fatal error in libpng when saving file '%s'.",
34270                               cimg_instance,
34271                               nfilename?nfilename:"(FILE*)");
34272       }
34273       png_write_image(png_ptr,imgData);
34274       png_write_end(png_ptr,info_ptr);
34275       png_destroy_write_struct(&png_ptr, &info_ptr);
34276 
34277       // Deallocate Image Write Memory
34278       cimg_forY(*this,n) delete[] imgData[n];
34279       delete[] imgData;
34280       if (!file) cimg::fclose(nfile);
34281       return *this;
34282 #endif
34283     }
34284 
34285     //! Save a file in PNG format
34286     const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
34287       return _save_png(0,filename,bytes_per_pixel);
34288     }
34289 
34290     //! Save a file in PNG format
34291     const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
34292       return _save_png(file,0,bytes_per_pixel);
34293     }
34294 
34295     //! Save the image as a PNM file.
34296     const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
34297       return _save_pnm(0,filename,bytes_per_pixel);
34298     }
34299 
34300     //! Save the image as a PNM file.
34301     const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
34302       return _save_pnm(file,0,bytes_per_pixel);
34303     }
34304 
34305     // Save the image as a PNM file (internal function).
34306     const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const {
34307       if (!file && !filename)
34308         throw CImgArgumentException(_cimg_instance
34309                                     "save_pnm() : Specified filename is (null).",
34310                                     cimg_instance);
34311       if (is_empty())
34312         throw CImgInstanceException(_cimg_instance
34313                                     "save_pnm() : Empty instance, for file '%s'.",
34314                                     cimg_instance,
34315                                     filename?filename:"(FILE*)");
34316 
34317       double stmin, stmax = (double)max_min(stmin);
34318       if (_depth>1)
34319         cimg::warn(_cimg_instance
34320                    "save_pnm() : Instance is volumetric, only the first slice will be saved in file '%s'.",
34321                    cimg_instance,
34322                    filename?filename:"(FILE*)");
34323 
34324       if (_spectrum>3)
34325         cimg::warn(_cimg_instance
34326                    "save_pnm() : Instance is multispectral, only the three first channels will be saved in file '%s'.",
34327                    cimg_instance,
34328                    filename?filename:"(FILE*)");
34329 
34330       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
34331         cimg::warn(_cimg_instance
34332                    "save_pnm() : Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
34333                    cimg_instance,
34334                    stmin,stmax,filename?filename:"(FILE*)");
34335 
34336       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
34337       const T
34338         *ptr_r = data(0,0,0,0),
34339         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
34340         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
34341       const unsigned int buf_size = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
34342 
34343       std::fprintf(nfile,"P%c\n%u %u\n%u\n",
34344                    (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
34345 
34346       switch (_spectrum) {
34347       case 1 : { // Scalar image
34348         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
34349           CImg<ucharT> buf(buf_size);
34350           for (int to_write = _width*_height; to_write>0; ) {
34351             const unsigned int N = cimg::min((unsigned int)to_write,buf_size);
34352             unsigned char *ptrd = buf._data;
34353             for (int i = (int)N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
34354             cimg::fwrite(buf._data,N,nfile);
34355             to_write-=N;
34356           }
34357         } else {             // Binary PGM 16 bits
34358           CImg<ushortT> buf(buf_size);
34359           for (int to_write = _width*_height; to_write>0; ) {
34360             const unsigned int N = cimg::min((unsigned int)to_write,buf_size);
34361             unsigned short *ptrd = buf._data;
34362             for (int i = (int)N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
34363             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
34364             cimg::fwrite(buf._data,N,nfile);
34365             to_write-=N;
34366           }
34367         }
34368       } break;
34369       case 2 : { // RG image
34370         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
34371           CImg<ucharT> buf(buf_size);
34372           for (int to_write = _width*_height; to_write>0; ) {
34373             const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
34374             unsigned char *ptrd = buf._data;
34375             for (int i = (int)N; i>0; --i) {
34376               *(ptrd++) = (unsigned char)*(ptr_r++);
34377               *(ptrd++) = (unsigned char)*(ptr_g++);
34378               *(ptrd++) = 0;
34379             }
34380             cimg::fwrite(buf._data,3*N,nfile);
34381             to_write-=N;
34382           }
34383         } else {             // Binary PPM 16 bits
34384           CImg<ushortT> buf(buf_size);
34385           for (int to_write = _width*_height; to_write>0; ) {
34386             const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
34387             unsigned short *ptrd = buf._data;
34388             for (int i = (int)N; i>0; --i) {
34389               *(ptrd++) = (unsigned short)*(ptr_r++);
34390               *(ptrd++) = (unsigned short)*(ptr_g++);
34391               *(ptrd++) = 0;
34392             }
34393             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
34394             cimg::fwrite(buf._data,3*N,nfile);
34395             to_write-=N;
34396           }
34397         }
34398       } break;
34399       default : { // RGB image
34400         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
34401           CImg<ucharT> buf(buf_size);
34402           for (int to_write = _width*_height; to_write>0; ) {
34403             const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
34404             unsigned char *ptrd = buf._data;
34405             for (int i = (int)N; i>0; --i) {
34406               *(ptrd++) = (unsigned char)*(ptr_r++);
34407               *(ptrd++) = (unsigned char)*(ptr_g++);
34408               *(ptrd++) = (unsigned char)*(ptr_b++);
34409             }
34410             cimg::fwrite(buf._data,3*N,nfile);
34411             to_write-=N;
34412           }
34413         } else {             // Binary PPM 16 bits
34414           CImg<ushortT> buf(buf_size);
34415           for (int to_write = _width*_height; to_write>0; ) {
34416             const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
34417             unsigned short *ptrd = buf._data;
34418             for (int i = (int)N; i>0; --i) {
34419               *(ptrd++) = (unsigned short)*(ptr_r++);
34420               *(ptrd++) = (unsigned short)*(ptr_g++);
34421               *(ptrd++) = (unsigned short)*(ptr_b++);
34422             }
34423             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
34424             cimg::fwrite(buf._data,3*N,nfile);
34425             to_write-=N;
34426           }
34427         }
34428       }
34429       }
34430       if (!file) cimg::fclose(nfile);
34431       return *this;
34432     }
34433 
34434     //! Save the image as a PFM file.
34435     const CImg<T>& save_pfm(const char *const filename) const {
34436       return _save_pfm(0,filename);
34437     }
34438 
34439     //! Save the image as a PFM file.
34440     const CImg<T>& save_pfm(std::FILE *const file) const {
34441       return _save_pfm(file,0);
34442     }
34443 
34444     // Save the image as a PFM file (internal function).
34445     const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
34446       if (!file && !filename)
34447         throw CImgArgumentException(_cimg_instance
34448                                     "save_pfm() : Specified filename is (null).",
34449                                     cimg_instance);
34450       if (is_empty())
34451         throw CImgInstanceException(_cimg_instance
34452                                     "save_pfm() : Empty instance, for file '%s'.",
34453                                     cimg_instance,
34454                                     filename?filename:"(FILE*)");
34455       if (_depth>1)
34456         cimg::warn(_cimg_instance
34457                    "save_pfm() : Instance is volumetric, only the first slice will be saved in file '%s'.",
34458                    cimg_instance,
34459                    filename?filename:"(FILE*)");
34460 
34461       if (_spectrum>3)
34462         cimg::warn(_cimg_instance
34463                    "save_pfm() : Instance image is multispectral, only the three first channels will be saved in file '%s'.",
34464                    cimg_instance,
34465                    filename?filename:"(FILE*)");
34466 
34467       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
34468       const T
34469         *ptr_r = data(0,0,0,0),
34470         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
34471         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
34472       const unsigned int buf_size = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
34473 
34474       std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
34475                    (_spectrum==1?'f':'F'),_width,_height);
34476 
34477       switch (_spectrum) {
34478       case 1 : { // Scalar image
34479         CImg<floatT> buf(buf_size);
34480         for (int to_write = _width*_height; to_write>0; ) {
34481           const unsigned int N = cimg::min((unsigned int)to_write,buf_size);
34482           float *ptrd = buf._data;
34483           for (int i = (int)N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
34484           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
34485           cimg::fwrite(buf._data,N,nfile);
34486           to_write-=N;
34487         }
34488       } break;
34489       case 2 : { // RG image
34490         CImg<floatT> buf(buf_size);
34491         for (int to_write = _width*_height; to_write>0; ) {
34492           const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
34493           float *ptrd = buf._data;
34494           for (int i = (int)N; i>0; --i) {
34495             *(ptrd++) = (float)*(ptr_r++);
34496             *(ptrd++) = (float)*(ptr_g++);
34497             *(ptrd++) = 0;
34498           }
34499           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
34500           cimg::fwrite(buf._data,3*N,nfile);
34501           to_write-=N;
34502         }
34503       } break;
34504       default : { // RGB image
34505         CImg<floatT> buf(buf_size);
34506         for (int to_write = _width*_height; to_write>0; ) {
34507           const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
34508           float *ptrd = buf._data;
34509           for (int i = (int)N; i>0; --i) {
34510             *(ptrd++) = (float)*(ptr_r++);
34511             *(ptrd++) = (float)*(ptr_g++);
34512             *(ptrd++) = (float)*(ptr_b++);
34513           }
34514           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
34515           cimg::fwrite(buf._data,3*N,nfile);
34516           to_write-=N;
34517         }
34518       }
34519       }
34520       if (!file) cimg::fclose(nfile);
34521       return *this;
34522     }
34523 
34524     // Save the image as a RGB file (internal).
34525     const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
34526       if (!file && !filename)
34527         throw CImgArgumentException(_cimg_instance
34528                                     "save_rgb() : Specified filename is (null).",
34529                                     cimg_instance);
34530       if (is_empty())
34531         throw CImgInstanceException(_cimg_instance
34532                                     "save_rgb() : Empty instance, for file '%s'.",
34533                                     cimg_instance,
34534                                     filename?filename:"(FILE*)");
34535       if (_spectrum!=3)
34536         cimg::warn(_cimg_instance
34537                    "save_rgb() : Instance image has not exactly 3 channels, for file '%s'.",
34538                    cimg_instance,
34539                    filename?filename:"(FILE*)");
34540 
34541       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
34542       const unsigned int wh = _width*_height;
34543       unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
34544       const T
34545         *ptr1 = data(0,0,0,0),
34546         *ptr2 = _spectrum>1?data(0,0,0,1):0,
34547         *ptr3 = _spectrum>2?data(0,0,0,2):0;
34548       switch (_spectrum) {
34549       case 1 : { // Scalar image
34550         for (unsigned int k = 0; k<wh; ++k) {
34551           const unsigned char val = (unsigned char)*(ptr1++);
34552           *(nbuffer++) = val;
34553           *(nbuffer++) = val;
34554           *(nbuffer++) = val;
34555         }
34556       } break;
34557       case 2 : { // RG image
34558         for (unsigned int k = 0; k<wh; ++k) {
34559           *(nbuffer++) = (unsigned char)(*(ptr1++));
34560           *(nbuffer++) = (unsigned char)(*(ptr2++));
34561           *(nbuffer++) = 0;
34562         }
34563       } break;
34564       default : { // RGB image
34565         for (unsigned int k = 0; k<wh; ++k) {
34566           *(nbuffer++) = (unsigned char)(*(ptr1++));
34567           *(nbuffer++) = (unsigned char)(*(ptr2++));
34568           *(nbuffer++) = (unsigned char)(*(ptr3++));
34569         }
34570       }
34571       }
34572       cimg::fwrite(buffer,3*wh,nfile);
34573       if (!file) cimg::fclose(nfile);
34574       delete[] buffer;
34575       return *this;
34576     }
34577 
34578     //! Save the image as a RGB file.
34579     const CImg<T>& save_rgb(const char *const filename) const {
34580       return _save_rgb(0,filename);
34581     }
34582 
34583     //! Save the image as a RGB file.
34584     const CImg<T>& save_rgb(std::FILE *const file) const {
34585       return _save_rgb(file,0);
34586     }
34587 
34588     // Save the image as a RGBA file (internal).
34589     const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
34590       if (!file && !filename)
34591         throw CImgArgumentException(_cimg_instance
34592                                     "save_rgba() : Specified filename is (null).",
34593                                     cimg_instance);
34594       if (is_empty())
34595         throw CImgInstanceException(_cimg_instance
34596                                     "save_rgba() : Empty instance, for file '%s'.",
34597                                     cimg_instance,
34598                                     filename?filename:"(FILE*)");
34599       if (_spectrum!=4)
34600         cimg::warn(_cimg_instance
34601                    "save_rgba() : Instance image has not exactly 4 channels, for file '%s'.",
34602                    cimg_instance,
34603                    filename?filename:"(FILE*)");
34604 
34605       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
34606       const unsigned int wh = _width*_height;
34607       unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
34608       const T
34609         *ptr1 = data(0,0,0,0),
34610         *ptr2 = _spectrum>1?data(0,0,0,1):0,
34611         *ptr3 = _spectrum>2?data(0,0,0,2):0,
34612         *ptr4 = _spectrum>3?data(0,0,0,3):0;
34613       switch (_spectrum) {
34614       case 1 : { // Scalar images
34615         for (unsigned int k = 0; k<wh; ++k) {
34616           const unsigned char val = (unsigned char)*(ptr1++);
34617           *(nbuffer++) = val;
34618           *(nbuffer++) = val;
34619           *(nbuffer++) = val;
34620           *(nbuffer++) = 255;
34621         }
34622       } break;
34623       case 2 : { // RG images
34624         for (unsigned int k = 0; k<wh; ++k) {
34625           *(nbuffer++) = (unsigned char)(*(ptr1++));
34626           *(nbuffer++) = (unsigned char)(*(ptr2++));
34627           *(nbuffer++) = 0;
34628           *(nbuffer++) = 255;
34629         }
34630       } break;
34631       case 3 : { // RGB images
34632         for (unsigned int k = 0; k<wh; ++k) {
34633           *(nbuffer++) = (unsigned char)(*(ptr1++));
34634           *(nbuffer++) = (unsigned char)(*(ptr2++));
34635           *(nbuffer++) = (unsigned char)(*(ptr3++));
34636           *(nbuffer++) = 255;
34637         }
34638       } break;
34639       default : { // RGBA images
34640         for (unsigned int k = 0; k<wh; ++k) {
34641           *(nbuffer++) = (unsigned char)(*(ptr1++));
34642           *(nbuffer++) = (unsigned char)(*(ptr2++));
34643           *(nbuffer++) = (unsigned char)(*(ptr3++));
34644           *(nbuffer++) = (unsigned char)(*(ptr4++));
34645         }
34646       }
34647       }
34648       cimg::fwrite(buffer,4*wh,nfile);
34649       if (!file) cimg::fclose(nfile);
34650       delete[] buffer;
34651       return *this;
34652     }
34653 
34654     //! Save the image as a RGBA file.
34655     const CImg<T>& save_rgba(const char *const filename) const {
34656       return _save_rgba(0,filename);
34657     }
34658 
34659     //! Save the image as a RGBA file.
34660     const CImg<T>& save_rgba(std::FILE *const file) const {
34661       return _save_rgba(file,0);
34662     }
34663 
34664     // Save a plane into a tiff file
34665 #ifdef cimg_use_tiff
34666 
34667 #define _cimg_save_tif(types,typed) \
34668     if (!std::strcmp(types,pixel_type())) { const typed foo = (typed)0; return _save_tiff(tif,directory,foo); }
34669 
34670     template<typename t>
34671     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const t& pixel_t) const {
34672       if (is_empty() || !tif || pixel_t) return *this;
34673       const char *const filename = TIFFFileName(tif);
34674       uint32 rowsperstrip = (uint32)-1;
34675       uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric, compression = COMPRESSION_NONE;
34676       if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
34677       else photometric = PHOTOMETRIC_MINISBLACK;
34678       TIFFSetDirectory(tif,directory);
34679       TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
34680       TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
34681       TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
34682       TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
34683       if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
34684       else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
34685       else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
34686       TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
34687       TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
34688       TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
34689       TIFFSetField(tif,TIFFTAG_COMPRESSION,compression);
34690       rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
34691       TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
34692       TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
34693       TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
34694       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
34695       if (buf) {
34696         for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
34697           uint32 nrow = (row + rowsperstrip>_height?_height-row:rowsperstrip);
34698           tstrip_t strip = TIFFComputeStrip(tif,row,0);
34699           tsize_t i = 0;
34700           for (unsigned int rr = 0; rr<nrow; ++rr)
34701             for (unsigned int cc = 0; cc<_width; ++cc)
34702               for (unsigned int vv = 0; vv<spp; ++vv)
34703                 buf[i++] = (t)(*this)(cc,row + rr,0,vv);
34704           if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
34705             throw CImgException(_cimg_instance
34706                                 "save_tiff() : Invalid strip writting when saving file '%s'.",
34707                                 cimg_instance,
34708                                 filename?filename:"(FILE*)");
34709         }
34710         _TIFFfree(buf);
34711       }
34712       TIFFWriteDirectory(tif);
34713       return (*this);
34714     }
34715 
34716     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory) const {
34717       _cimg_save_tif("bool",unsigned char);
34718       _cimg_save_tif("char",char);
34719       _cimg_save_tif("unsigned char",unsigned char);
34720       _cimg_save_tif("short",short);
34721       _cimg_save_tif("unsigned short",unsigned short);
34722       _cimg_save_tif("int",int);
34723       _cimg_save_tif("unsigned int",unsigned int);
34724       _cimg_save_tif("long",int);
34725       _cimg_save_tif("unsigned long",unsigned int);
34726       _cimg_save_tif("float",float);
34727       _cimg_save_tif("double",float);
34728       const char *const filename = TIFFFileName(tif);
34729       throw CImgException(_cimg_instance
34730                           "save_tiff() : Unsupported pixel type '%s' for file '%s'.",
34731                           cimg_instance,
34732                           pixel_type(),filename?filename:"(FILE*)");
34733       return *this;
34734     }
34735 #endif
34736 
34737     //! Save a file in TIFF format.
34738     const CImg<T>& save_tiff(const char *const filename) const {
34739       if (!filename)
34740         throw CImgArgumentException(_cimg_instance
34741                                     "save_tiff() : Specified filename is (null).",
34742                                     cimg_instance);
34743       if (is_empty())
34744         throw CImgInstanceException(_cimg_instance
34745                                     "save_tiff() : Empty instance, for file '%s'.",
34746                                     cimg_instance,
34747                                     filename);
34748 
34749 #ifdef cimg_use_tiff
34750       TIFF *tif = TIFFOpen(filename,"w");
34751       if (tif) {
34752         cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z);
34753         TIFFClose(tif);
34754       } else throw CImgException(_cimg_instance
34755                                  "save_tiff() : Failed to open file '%s' for writing.",
34756                                  cimg_instance,
34757                                  filename);
34758 #else
34759       return save_other(filename);
34760 #endif
34761       return *this;
34762     }
34763 
34764     //! Save the image as an ANALYZE7.5 or NIFTI file.
34765     const CImg<T>& save_analyze(const char *const filename, const float *const voxsize=0) const {
34766       if (!filename)
34767         throw CImgArgumentException(_cimg_instance
34768                                     "save_analyze() : Specified filename is (null).",
34769                                     cimg_instance);
34770       if (is_empty())
34771         throw CImgInstanceException(_cimg_instance
34772                                     "save_analyze() : Empty instance, for file '%s'.",
34773                                     cimg_instance,
34774                                     filename);
34775 
34776       std::FILE *file;
34777       char header[348] = { 0 }, hname[1024] = { 0 }, iname[1024] = { 0 };
34778       const char *const ext = cimg::split_filename(filename);
34779       short datatype=-1;
34780       std::memset(header,0,348);
34781       if (!ext[0]) { std::sprintf(hname,"%s.hdr",filename); std::sprintf(iname,"%s.img",filename); }
34782       if (!cimg::strncasecmp(ext,"hdr",3)) {
34783         std::strcpy(hname,filename); std::strcpy(iname,filename); std::sprintf(iname+std::strlen(iname)-3,"img");
34784       }
34785       if (!cimg::strncasecmp(ext,"img",3)) {
34786         std::strcpy(hname,filename); std::strcpy(iname,filename); std::sprintf(hname+std::strlen(iname)-3,"hdr");
34787       }
34788       if (!cimg::strncasecmp(ext,"nii",3)) {
34789         std::strcpy(hname,filename); iname[0] = 0;
34790       }
34791       int *const iheader = (int*)header;
34792       iheader[0] = 348;
34793       std::strcpy(header+4,"CImg");
34794       std::strcpy(header+14," ");
34795       ((short*)(header+36))[0] = 4096;
34796       ((char*)(header+38))[0] = 114;
34797       ((short*)(header+40))[0] = 4;
34798       ((short*)(header+40))[1] = _width;
34799       ((short*)(header+40))[2] = _height;
34800       ((short*)(header+40))[3] = _depth;
34801       ((short*)(header+40))[4] = _spectrum;
34802       if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
34803       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
34804       if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
34805       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
34806       if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
34807       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
34808       if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
34809       if (!cimg::strcasecmp(pixel_type(),"unsigned long")) datatype = 8;
34810       if (!cimg::strcasecmp(pixel_type(),"long")) datatype = 8;
34811       if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
34812       if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
34813       if (datatype<0)
34814         throw CImgIOException(_cimg_instance
34815                               "save_analyze() : Unsupported pixel type '%s' for file '%s'.",
34816                               cimg_instance,
34817                               pixel_type(),filename);
34818 
34819       ((short*)(header+70))[0] = datatype;
34820       ((short*)(header+72))[0] = sizeof(T);
34821       ((float*)(header+112))[0] = 1;
34822       ((float*)(header+76))[0] = 0;
34823       if (voxsize) {
34824         ((float*)(header+76))[1] = voxsize[0];
34825         ((float*)(header+76))[2] = voxsize[1];
34826         ((float*)(header+76))[3] = voxsize[2];
34827       } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1;
34828       file = cimg::fopen(hname,"wb");
34829       cimg::fwrite(header,348,file);
34830       if (iname[0]) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
34831       cimg::fwrite(_data,size(),file);
34832       cimg::fclose(file);
34833       return *this;
34834     }
34835 
34836     //! Save the image as a .cimg file.
34837     const CImg<T>& save_cimg(const char *const filename, const bool compress=false) const {
34838       CImgList<T>(*this,true).save_cimg(filename,compress);
34839       return *this;
34840     }
34841 
34842     // Save the image as a .cimg file.
34843     const CImg<T>& save_cimg(std::FILE *const file, const bool compress=false) const {
34844       CImgList<T>(*this,true).save_cimg(file,compress);
34845       return *this;
34846     }
34847 
34848     //! Insert the image into an existing .cimg file, at specified coordinates.
34849     const CImg<T>& save_cimg(const char *const filename,
34850                              const unsigned int n0,
34851                              const unsigned int x0, const unsigned int y0,
34852                              const unsigned int z0, const unsigned int c0) const {
34853       CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
34854       return *this;
34855     }
34856 
34857     //! Insert the image into an existing .cimg file, at specified coordinates.
34858     const CImg<T>& save_cimg(std::FILE *const file,
34859                              const unsigned int n0,
34860                              const unsigned int x0, const unsigned int y0,
34861                              const unsigned int z0, const unsigned int c0) const {
34862       CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
34863       return *this;
34864     }
34865 
34866     //! Save an empty .cimg file with specified dimensions.
34867     static void save_empty_cimg(const char *const filename,
34868                                 const unsigned int dx, const unsigned int dy=1,
34869                                 const unsigned int dz=1, const unsigned int dc=1) {
34870       return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
34871     }
34872 
34873     //! Save an empty .cimg file with specified dimensions.
34874     static void save_empty_cimg(std::FILE *const file,
34875                                 const unsigned int dx, const unsigned int dy=1,
34876                                 const unsigned int dz=1, const unsigned int dc=1) {
34877       return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
34878     }
34879 
34880     // Save the image as an INRIMAGE-4 file (internal).
34881     const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxsize) const {
34882       if (!file && !filename)
34883         throw CImgArgumentException(_cimg_instance
34884                                     "save_inr() : Specified filename is (null).",
34885                                     cimg_instance);
34886       if (is_empty())
34887         throw CImgInstanceException(_cimg_instance
34888                                     "save_inr() : Empty instance, for file '%s'.",
34889                                     cimg_instance,
34890                                     filename?filename:"(FILE*)");
34891 
34892       int inrpixsize=-1;
34893       const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
34894       if (!cimg::strcasecmp(pixel_type(),"unsigned char"))  { inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; }
34895       if (!cimg::strcasecmp(pixel_type(),"char"))           { inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; }
34896       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; }
34897       if (!cimg::strcasecmp(pixel_type(),"short"))          { inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; }
34898       if (!cimg::strcasecmp(pixel_type(),"unsigned int"))   { inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; }
34899       if (!cimg::strcasecmp(pixel_type(),"int"))            { inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; }
34900       if (!cimg::strcasecmp(pixel_type(),"float"))          { inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; }
34901       if (!cimg::strcasecmp(pixel_type(),"double"))         { inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; }
34902       if (inrpixsize<=0)
34903         throw CImgIOException(_cimg_instance
34904                               "save_inr() : Unsupported pixel type '%s' for file '%s'",
34905                               cimg_instance,
34906                               pixel_type(),filename?filename:"(FILE*)");
34907 
34908       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
34909       char header[257] = { 0 };
34910       int err = std::sprintf(header,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",_width,_height,_depth,_spectrum);
34911       if (voxsize) err+=std::sprintf(header+err,"VX=%g\nVY=%g\nVZ=%g\n",voxsize[0],voxsize[1],voxsize[2]);
34912       err+=std::sprintf(header+err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
34913       std::memset(header+err,'\n',252-err);
34914       std::memcpy(header+252,"##}\n",4);
34915       cimg::fwrite(header,256,nfile);
34916       cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
34917       if (!file) cimg::fclose(nfile);
34918       return *this;
34919     }
34920 
34921     //! Save the image as an INRIMAGE-4 file.
34922     const CImg<T>& save_inr(const char *const filename, const float *const voxsize=0) const {
34923       return _save_inr(0,filename,voxsize);
34924     }
34925 
34926     //! Save the image as an INRIMAGE-4 file.
34927     const CImg<T>& save_inr(std::FILE *const file, const float *const voxsize=0) const {
34928       return _save_inr(file,0,voxsize);
34929     }
34930 
34931     //! Save the image as a EXR file.
34932     const CImg<T>& save_exr(const char *const filename) const {
34933       if (!filename)
34934         throw CImgArgumentException(_cimg_instance
34935                                     "save_exr() : Specified filename is (null).",
34936                                     cimg_instance);
34937       if (is_empty())
34938         throw CImgInstanceException(_cimg_instance
34939                                     "save_exr() : Empty instance, for file '%s'.",
34940                                     cimg_instance,
34941                                     filename);
34942       if (_depth>1)
34943         cimg::warn(_cimg_instance
34944                    "save_exr() : Instance is volumetric, only the first slice will be saved in file '%s'.",
34945                    cimg_instance,
34946                    filename);
34947 
34948 #ifndef cimg_use_openexr
34949       return save_other(filename);
34950 #else
34951       Imf::Rgba *const ptrd0 = new Imf::Rgba[_width*_height], *ptrd = ptrd0, rgba;
34952       switch (_spectrum) {
34953       case 1 : { // Grayscale image.
34954         for (const T *ptr_r = data(), *const ptr_e = ptr_r + _width*_height; ptr_r<ptr_e;) {
34955           rgba.r = rgba.g = rgba.b = (half)(*(ptr_r++));
34956           rgba.a = (half)1;
34957           *(ptrd++) = rgba;
34958         }
34959       } break;
34960       case 2 : { // RG image.
34961         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *const ptr_e = ptr_r + _width*_height; ptr_r<ptr_e; ) {
34962           rgba.r = (half)(*(ptr_r++));
34963           rgba.g = (half)(*(ptr_g++));
34964           rgba.b = (half)0;
34965           rgba.a = (half)1;
34966           *(ptrd++) = rgba;
34967         }
34968       } break;
34969       case 3 : { // RGB image.
34970         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *const ptr_e = ptr_r + _width*_height; ptr_r<ptr_e;) {
34971           rgba.r = (half)(*(ptr_r++));
34972           rgba.g = (half)(*(ptr_g++));
34973           rgba.b = (half)(*(ptr_b++));
34974           rgba.a = (half)1;
34975           *(ptrd++) = rgba;
34976         }
34977       } break;
34978       default : { // RGBA image.
34979         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3),
34980                *const ptr_e = ptr_r + _width*_height; ptr_r<ptr_e;) {
34981           rgba.r = (half)(*(ptr_r++));
34982           rgba.g = (half)(*(ptr_g++));
34983           rgba.b = (half)(*(ptr_b++));
34984           rgba.a = (half)(*(ptr_a++));
34985           *(ptrd++) = rgba;
34986         }
34987       } break;
34988       }
34989       Imf::RgbaOutputFile outFile(filename,_width,_height,
34990                                   _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?Imf::WRITE_RGB:Imf::WRITE_RGBA);
34991       outFile.setFrameBuffer(ptrd0,1,_width);
34992       outFile.writePixels(_height);
34993       delete[] ptrd0;
34994       return *this;
34995 #endif
34996     }
34997 
34998     // Save the image as a PANDORE-5 file (internal).
34999     unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
35000       unsigned int nbdims = 0;
35001       if (id==2 || id==3 || id==4) { dims[0] = 1; dims[1] = _width;  nbdims = 2; }
35002       if (id==5 || id==6 || id==7) { dims[0] = 1; dims[1] = _height; dims[2] = _width;  nbdims=3; }
35003       if (id==8 || id==9 || id==10) { dims[0] = _spectrum; dims[1] = _depth;  dims[2] = _height; dims[3] = _width; nbdims = 4; }
35004       if (id==16 || id==17 || id==18) { dims[0] = 3; dims[1] = _height; dims[2] = _width;  dims[3] = colorspace; nbdims = 4; }
35005       if (id==19 || id==20 || id==21) { dims[0] = 3; dims[1] = _depth;  dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; }
35006       if (id==22 || id==23 || id==25) { dims[0] = _spectrum; dims[1] = _width;  nbdims = 2; }
35007       if (id==26 || id==27 || id==29) { dims[0] = _spectrum; dims[1] = _height; dims[2] = _width;  nbdims=3; }
35008       if (id==30 || id==31 || id==33) { dims[0] = _spectrum; dims[1] = _depth;  dims[2] = _height; dims[3] = _width; nbdims = 4; }
35009       return nbdims;
35010     }
35011 
35012     const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename, const unsigned int colorspace) const {
35013 
35014 #define __cimg_save_pandore_case(dtype) \
35015        dtype *buffer = new dtype[size()]; \
35016        const T *ptrs = _data; \
35017        cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
35018        buffer-=size(); \
35019        cimg::fwrite(buffer,size(),nfile); \
35020        delete[] buffer
35021 
35022 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
35023       if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
35024         unsigned int *iheader = (unsigned int*)(header+12); \
35025         nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
35026         cimg::fwrite(header,36,nfile); \
35027         if (sizeof(unsigned long)==4) { unsigned long ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
35028         else if (sizeof(unsigned int)==4) { unsigned int ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
35029         else if (sizeof(unsigned short)==4) { unsigned short ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
35030         else throw CImgIOException(_cimg_instance \
35031                                    "save_pandore() : Unsupported datatype for file '%s'.",\
35032                                    cimg_instance, \
35033                                    filename?filename:"(FILE*)"); \
35034         if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
35035           __cimg_save_pandore_case(unsigned char); \
35036         } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
35037           if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
35038           else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
35039           else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
35040           else throw CImgIOException(_cimg_instance \
35041                                      "save_pandore() : Unsupported datatype for file '%s'.",\
35042                                      cimg_instance, \
35043                                      filename?filename:"(FILE*)"); \
35044         } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
35045           if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
35046           else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
35047           else throw CImgIOException(_cimg_instance \
35048                                      "save_pandore() : Unsupported datatype for file '%s'.",\
35049                                      cimg_instance, \
35050                                      filename?filename:"(FILE*)"); \
35051         } \
35052         saved = true; \
35053       }
35054 
35055       if (!file && !filename)
35056         throw CImgArgumentException(_cimg_instance
35057                                     "save_pandore() : Specified filename is (null).",
35058                                     cimg_instance);
35059       if (is_empty())
35060         throw CImgInstanceException(_cimg_instance
35061                                     "save_pandore() : Empty instance, for file '%s'.",
35062                                     cimg_instance,
35063                                     filename?filename:"(FILE*)");
35064 
35065       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
35066       unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
35067                                    0,0,0,0,'C','I','m','g',0,0,0,0,0,'N','o',' ','d','a','t','e',0,0,0,0 };
35068       unsigned int nbdims, dims[5] = { 0 };
35069       bool saved = false;
35070       _cimg_save_pandore_case(1,1,1,"unsigned char",2);
35071       _cimg_save_pandore_case(1,1,1,"char",3);
35072       _cimg_save_pandore_case(1,1,1,"short",3);
35073       _cimg_save_pandore_case(1,1,1,"unsigned short",3);
35074       _cimg_save_pandore_case(1,1,1,"unsigned int",3);
35075       _cimg_save_pandore_case(1,1,1,"int",3);
35076       _cimg_save_pandore_case(1,1,1,"unsigned long",4);
35077       _cimg_save_pandore_case(1,1,1,"long",3);
35078       _cimg_save_pandore_case(1,1,1,"float",4);
35079       _cimg_save_pandore_case(1,1,1,"double",4);
35080 
35081       _cimg_save_pandore_case(0,1,1,"unsigned char",5);
35082       _cimg_save_pandore_case(0,1,1,"char",6);
35083       _cimg_save_pandore_case(0,1,1,"short",6);
35084       _cimg_save_pandore_case(0,1,1,"unsigned short",6);
35085       _cimg_save_pandore_case(0,1,1,"unsigned int",6);
35086       _cimg_save_pandore_case(0,1,1,"int",6);
35087       _cimg_save_pandore_case(0,1,1,"unsigned long",7);
35088       _cimg_save_pandore_case(0,1,1,"long",6);
35089       _cimg_save_pandore_case(0,1,1,"float",7);
35090       _cimg_save_pandore_case(0,1,1,"double",7);
35091 
35092       _cimg_save_pandore_case(0,0,1,"unsigned char",8);
35093       _cimg_save_pandore_case(0,0,1,"char",9);
35094       _cimg_save_pandore_case(0,0,1,"short",9);
35095       _cimg_save_pandore_case(0,0,1,"unsigned short",9);
35096       _cimg_save_pandore_case(0,0,1,"unsigned int",9);
35097       _cimg_save_pandore_case(0,0,1,"int",9);
35098       _cimg_save_pandore_case(0,0,1,"unsigned long",10);
35099       _cimg_save_pandore_case(0,0,1,"long",9);
35100       _cimg_save_pandore_case(0,0,1,"float",10);
35101       _cimg_save_pandore_case(0,0,1,"double",10);
35102 
35103       _cimg_save_pandore_case(0,1,3,"unsigned char",16);
35104       _cimg_save_pandore_case(0,1,3,"char",17);
35105       _cimg_save_pandore_case(0,1,3,"short",17);
35106       _cimg_save_pandore_case(0,1,3,"unsigned short",17);
35107       _cimg_save_pandore_case(0,1,3,"unsigned int",17);
35108       _cimg_save_pandore_case(0,1,3,"int",17);
35109       _cimg_save_pandore_case(0,1,3,"unsigned long",18);
35110       _cimg_save_pandore_case(0,1,3,"long",17);
35111       _cimg_save_pandore_case(0,1,3,"float",18);
35112       _cimg_save_pandore_case(0,1,3,"double",18);
35113 
35114       _cimg_save_pandore_case(0,0,3,"unsigned char",19);
35115       _cimg_save_pandore_case(0,0,3,"char",20);
35116       _cimg_save_pandore_case(0,0,3,"short",20);
35117       _cimg_save_pandore_case(0,0,3,"unsigned short",20);
35118       _cimg_save_pandore_case(0,0,3,"unsigned int",20);
35119       _cimg_save_pandore_case(0,0,3,"int",20);
35120       _cimg_save_pandore_case(0,0,3,"unsigned long",21);
35121       _cimg_save_pandore_case(0,0,3,"long",20);
35122       _cimg_save_pandore_case(0,0,3,"float",21);
35123       _cimg_save_pandore_case(0,0,3,"double",21);
35124 
35125       _cimg_save_pandore_case(1,1,0,"unsigned char",22);
35126       _cimg_save_pandore_case(1,1,0,"char",23);
35127       _cimg_save_pandore_case(1,1,0,"short",23);
35128       _cimg_save_pandore_case(1,1,0,"unsigned short",23);
35129       _cimg_save_pandore_case(1,1,0,"unsigned int",23);
35130       _cimg_save_pandore_case(1,1,0,"int",23);
35131       _cimg_save_pandore_case(1,1,0,"unsigned long",25);
35132       _cimg_save_pandore_case(1,1,0,"long",23);
35133       _cimg_save_pandore_case(1,1,0,"float",25);
35134       _cimg_save_pandore_case(1,1,0,"double",25);
35135 
35136       _cimg_save_pandore_case(0,1,0,"unsigned char",26);
35137       _cimg_save_pandore_case(0,1,0,"char",27);
35138       _cimg_save_pandore_case(0,1,0,"short",27);
35139       _cimg_save_pandore_case(0,1,0,"unsigned short",27);
35140       _cimg_save_pandore_case(0,1,0,"unsigned int",27);
35141       _cimg_save_pandore_case(0,1,0,"int",27);
35142       _cimg_save_pandore_case(0,1,0,"unsigned long",29);
35143       _cimg_save_pandore_case(0,1,0,"long",27);
35144       _cimg_save_pandore_case(0,1,0,"float",29);
35145       _cimg_save_pandore_case(0,1,0,"double",29);
35146 
35147       _cimg_save_pandore_case(0,0,0,"unsigned char",30);
35148       _cimg_save_pandore_case(0,0,0,"char",31);
35149       _cimg_save_pandore_case(0,0,0,"short",31);
35150       _cimg_save_pandore_case(0,0,0,"unsigned short",31);
35151       _cimg_save_pandore_case(0,0,0,"unsigned int",31);
35152       _cimg_save_pandore_case(0,0,0,"int",31);
35153       _cimg_save_pandore_case(0,0,0,"unsigned long",33);
35154       _cimg_save_pandore_case(0,0,0,"long",31);
35155       _cimg_save_pandore_case(0,0,0,"float",33);
35156       _cimg_save_pandore_case(0,0,0,"double",33);
35157 
35158       if (!file) cimg::fclose(nfile);
35159       return *this;
35160     }
35161 
35162     //! Save the image as a PANDORE-5 file.
35163     const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
35164       return _save_pandore(0,filename,colorspace);
35165     }
35166 
35167     //! Save the image as a PANDORE-5 file.
35168     const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
35169       return _save_pandore(file,0,colorspace);
35170     }
35171 
35172    // Save the image as a RAW file (internal).
35173     const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool multiplexed) const {
35174       if (!file && !filename)
35175         throw CImgArgumentException(_cimg_instance
35176                                     "save_raw() : Specified filename is (null).",
35177                                     cimg_instance);
35178       if (is_empty())
35179         throw CImgInstanceException(_cimg_instance
35180                                     "save_raw() : empty instance, for file '%s'.",
35181                                     cimg_instance,
35182                                     filename?filename:"(FILE*)");
35183 
35184       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
35185       if (!multiplexed) cimg::fwrite(_data,size(),nfile);
35186       else {
35187         CImg<T> buf(_spectrum);
35188         cimg_forXYZ(*this,x,y,z) {
35189           cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
35190           cimg::fwrite(buf._data,_spectrum,nfile);
35191         }
35192       }
35193       if (!file) cimg::fclose(nfile);
35194       return *this;
35195     }
35196 
35197     //! Save the image as a RAW file.
35198     const CImg<T>& save_raw(const char *const filename, const bool multiplexed=false) const {
35199       return _save_raw(0,filename,multiplexed);
35200     }
35201 
35202     //! Save the image as a RAW file.
35203     const CImg<T>& save_raw(std::FILE *const file, const bool multiplexed=false) const {
35204       return _save_raw(file,0,multiplexed);
35205     }
35206 
35207     //! Save the image as a video sequence file, using FFMPEG library.
35208     const CImg<T>& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35209                                const unsigned int fps=25) const {
35210       if (!filename)
35211         throw CImgArgumentException(_cimg_instance
35212                                     "save_ffmpeg() : Specified filename is (null).",
35213                                     cimg_instance);
35214       if (is_empty())
35215         throw CImgInstanceException(_cimg_instance
35216                                     "save_ffmpeg() : Empty instance, for file '%s'.",
35217                                     cimg_instance,
35218                                     filename);
35219       if (!fps)
35220         throw CImgArgumentException(_cimg_instance
35221                                     "save_ffmpeg() : Invalid specified framerate 0, for file '%s'.",
35222                                     cimg_instance,
35223                                     filename);
35224 
35225 #ifndef cimg_use_ffmpeg
35226       return save_ffmpeg_external(filename,first_frame,last_frame);
35227 #else
35228       get_split('z').save_ffmpeg(filename,first_frame,last_frame,fps);
35229 #endif
35230       return *this;
35231     }
35232 
35233     //! Save the image as a YUV video sequence file.
35234     const CImg<T>& save_yuv(const char *const filename, const bool rgb2yuv=true) const {
35235       get_split('z').save_yuv(filename,rgb2yuv);
35236       return *this;
35237     }
35238 
35239     //! Save the image as a YUV video sequence file.
35240     const CImg<T>& save_yuv(std::FILE *const file, const bool rgb2yuv=true) const {
35241       get_split('z').save_yuv(file,rgb2yuv);
35242       return *this;
35243     }
35244 
35245     // Save OFF files (internal).
35246     template<typename tf, typename tc>
35247     const CImg<T>& _save_off(std::FILE *const file, const char *const filename,
35248                              const CImgList<tf>& primitives, const CImgList<tc>& colors) const {
35249       if (!file && !filename)
35250         throw CImgArgumentException(_cimg_instance
35251                                     "save_off() : Specified filename is (null).",
35252                                     cimg_instance);
35253       if (is_empty())
35254         throw CImgInstanceException(_cimg_instance
35255                                     "save_off() : Empty instance, for file '%s'.",
35256                                     cimg_instance,
35257                                     filename?filename:"(FILE*)");
35258 
35259       CImgList<T> opacities;
35260       char error_message[1024] = { 0 };
35261       if (!is_object3d(primitives,colors,opacities,true,error_message))
35262         throw CImgInstanceException(_cimg_instance
35263                                     "save_off() : Invalid specified 3d object, for file '%s' (%s).",
35264                                     cimg_instance,
35265                                     filename?filename:"(FILE*)",error_message);
35266 
35267       const CImg<tc> default_color(1,3,1,1,200);
35268       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
35269       unsigned int supported_primitives = 0;
35270       cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
35271       std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
35272       cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
35273       cimglist_for(primitives,l) {
35274         const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
35275         const unsigned int psiz = primitives[l].size(), csiz = color.size();
35276         const float r = color[0]/255.0f, g = (csiz>1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f;
35277         switch (psiz) {
35278         case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",(unsigned int)primitives(l,0),r,g,b); break;
35279         case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
35280         case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
35281                               (unsigned int)primitives(l,1),r,g,b); break;
35282         case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
35283                               (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
35284         case 6 : {
35285           const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
35286           const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
35287           std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
35288         } break;
35289         case 9 : {
35290           const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
35291           const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
35292           std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
35293                        (unsigned int)primitives(l,1),rt,gt,bt);
35294         } break;
35295         case 12 : {
35296           const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
35297           const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
35298           std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
35299                        (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
35300         } break;
35301         }
35302       }
35303       if (!file) cimg::fclose(nfile);
35304       return *this;
35305     }
35306 
35307     //! Save OFF files.
35308     template<typename tf, typename tc>
35309     const CImg<T>& save_off(const char *const filename,
35310                             const CImgList<tf>& primitives, const CImgList<tc>& colors) const {
35311       return _save_off(0,filename,primitives,colors);
35312     }
35313 
35314     //! Save OFF files.
35315     template<typename tf, typename tc>
35316     const CImg<T>& save_off(std::FILE *const file,
35317                             const CImgList<tf>& primitives, const CImgList<tc>& colors) const {
35318       return _save_off(file,0,primitives,colors);
35319     }
35320 
35321     //! Save the image as a video sequence file, using the external tool 'ffmpeg'.
35322     const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35323                                         const char *const codec="mpeg2video") const {
35324       if (!filename)
35325         throw CImgArgumentException(_cimg_instance
35326                                     "save_ffmpeg_external() : Specified filename is (null).",
35327                                     cimg_instance);
35328       if (is_empty())
35329         throw CImgInstanceException(_cimg_instance
35330                                     "save_ffmpeg_external() : Empty instance, for file '%s'.",
35331                                     cimg_instance,
35332                                     filename);
35333 
35334       get_split('z').save_ffmpeg_external(filename,first_frame,last_frame,codec);
35335       return *this;
35336     }
35337 
35338     //! Save the image using GraphicsMagick's gm.
35339     /** Function that saves the image for other file formats that are not natively handled by CImg,
35340         using the tool 'gm' from the GraphicsMagick package.\n
35341         This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install
35342         the GraphicsMagick package in order to get
35343         this function working properly (see http://www.graphicsmagick.org ).
35344     **/
35345     const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
35346       if (!filename)
35347         throw CImgArgumentException(_cimg_instance
35348                                     "save_graphicsmagick_external() : Specified filename is (null).",
35349                                     cimg_instance);
35350       if (is_empty())
35351         throw CImgInstanceException(_cimg_instance
35352                                     "save_graphicsmagick_external() : Empty instance, for file '%s'.",
35353                                     cimg_instance,
35354                                     filename);
35355 
35356       char command[1024] = { 0 }, filetmp[512] = { 0 };
35357       std::FILE *file;
35358       do {
35359         std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm");
35360         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
35361       } while (file);
35362       save_pnm(filetmp);
35363       std::sprintf(command,"%s -quality %u%% \"%s\" \"%s\"",cimg::graphicsmagick_path(),quality,filetmp,filename);
35364       cimg::system(command);
35365       file = std::fopen(filename,"rb");
35366       if (!file)
35367         throw CImgIOException(_cimg_instance
35368                               "save_graphicsmagick_external() : Failed to save file '%s' with external command 'gm'.",
35369                               cimg_instance,
35370                               filename);
35371 
35372       if (file) cimg::fclose(file);
35373       std::remove(filetmp);
35374       return *this;
35375     }
35376 
35377     //! Save an image as a gzipped file, using external tool 'gzip'.
35378     const CImg<T>& save_gzip_external(const char *const filename) const {
35379       if (!filename)
35380         throw CImgArgumentException(_cimg_instance
35381                                     "save_gzip_external() : Specified filename is (null).",
35382                                     cimg_instance);
35383       if (is_empty())
35384         throw CImgInstanceException(_cimg_instance
35385                                     "save_gzip_external() : Empty instance, for file '%s'.",
35386                                     cimg_instance,
35387                                     filename);
35388 
35389       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
35390       const char
35391         *ext = cimg::split_filename(filename,body),
35392         *ext2 = cimg::split_filename(body,0);
35393       std::FILE *file;
35394       do {
35395         if (!cimg::strcasecmp(ext,"gz")) {
35396           if (*ext2) std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
35397           else std::sprintf(filetmp,"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
35398         } else {
35399           if (*ext) std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
35400           else std::sprintf(filetmp,"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
35401         }
35402         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
35403       } while (file);
35404       save(filetmp);
35405       std::sprintf(command,"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename);
35406       cimg::system(command);
35407       file = std::fopen(filename,"rb");
35408       if (!file)
35409         throw CImgIOException(_cimg_instance
35410                               "save_gzip_external() : Failed to save file '%s' with external command 'gzip'.",
35411                               cimg_instance,
35412                               filename);
35413 
35414       else cimg::fclose(file);
35415       std::remove(filetmp);
35416       return *this;
35417     }
35418 
35419     //! Save the image using ImageMagick's convert.
35420     /** Function that saves the image for other file formats that are not natively handled by CImg,
35421         using the tool 'convert' from the ImageMagick package.\n
35422         This is the case for all compressed image formats (GIF,PNG,JPG,TIF, ...). You need to install
35423         the ImageMagick package in order to get
35424         this function working properly (see http://www.imagemagick.org ).
35425     **/
35426     const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
35427       if (!filename)
35428         throw CImgArgumentException(_cimg_instance
35429                                     "save_imagemagick_external() : Specified filename is (null).",
35430                                     cimg_instance);
35431       if (is_empty())
35432         throw CImgInstanceException(_cimg_instance
35433                                     "save_imagemagick_external() : Empty instance, for file '%s'.",
35434                                     cimg_instance,
35435                                     filename);
35436 
35437       char command[1024] = { 0 }, filetmp[512] = { 0 };
35438       std::FILE *file;
35439       do {
35440         std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm");
35441         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
35442       } while (file);
35443       save_pnm(filetmp);
35444       std::sprintf(command,"%s -quality %u%% \"%s\" \"%s\"",cimg::imagemagick_path(),quality,filetmp,filename);
35445       cimg::system(command);
35446       file = std::fopen(filename,"rb");
35447       if (!file)
35448         throw CImgIOException(_cimg_instance
35449                               "save_imagemagick_external() : Failed to save file '%s' with external command 'convert'.",
35450                               cimg_instance,
35451                               filename);
35452 
35453       if (file) cimg::fclose(file);
35454       std::remove(filetmp);
35455       return *this;
35456     }
35457 
35458     //! Save an image as a Dicom file (need '(X)Medcon' : http://xmedcon.sourceforge.net )
35459     const CImg<T>& save_medcon_external(const char *const filename) const {
35460       if (!filename)
35461         throw CImgArgumentException(_cimg_instance
35462                                     "save_medcon_external() : Specified filename is (null).",
35463                                     cimg_instance);
35464       if (is_empty())
35465         throw CImgInstanceException(_cimg_instance
35466                                     "save_medcon_external() : Empty instance, for file '%s'.",
35467                                     cimg_instance,
35468                                     filename);
35469 
35470       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
35471       std::FILE *file;
35472       do {
35473         std::sprintf(filetmp,"%s.hdr",cimg::filenamerand());
35474         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
35475       } while (file);
35476       save_analyze(filetmp);
35477       std::sprintf(command,"%s -w -c dicom -o %s -f %s",cimg::medcon_path(),filename,filetmp);
35478       cimg::system(command);
35479       std::remove(filetmp);
35480       cimg::split_filename(filetmp,body);
35481       std::sprintf(filetmp,"%s.img",body);
35482       std::remove(filetmp);
35483       std::sprintf(command,"m000-%s",filename);
35484       file = std::fopen(command,"rb");
35485       if (!file) {
35486         cimg::fclose(cimg::fopen(filename,"r"));
35487         throw CImgIOException(_cimg_instance
35488                               "save_medcon_external() : Failed to save file '%s' with external command 'medcon'.",
35489                               cimg_instance,
35490                               filename);
35491 
35492       } else cimg::fclose(file);
35493       std::rename(command,filename);
35494       return *this;
35495     }
35496 
35497     // Try to save the image if other extension is provided.
35498     const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
35499       if (!filename)
35500         throw CImgArgumentException(_cimg_instance
35501                                     "save_other() : Specified filename is (null).",
35502                                     cimg_instance);
35503       if (is_empty())
35504         throw CImgInstanceException(_cimg_instance
35505                                     "save_other() : Empty instance, for file '%s'.",
35506                                     cimg_instance,
35507                                     filename);
35508 
35509       const unsigned int omode = cimg::exception_mode();
35510       bool is_saved = true;
35511       cimg::exception_mode() = 0;
35512       try { save_magick(filename); }
35513       catch (CImgException&) {
35514         try { save_imagemagick_external(filename,quality); }
35515         catch (CImgException&) {
35516           try { save_graphicsmagick_external(filename,quality); }
35517           catch (CImgException&) {
35518             is_saved = false;
35519           }
35520         }
35521       }
35522       cimg::exception_mode() = omode;
35523       if (!is_saved)
35524         throw CImgIOException(_cimg_instance
35525                               "save_other() : Failed to save file '%s'. Format is not natively supported, and no external commands succeeded.",
35526                               cimg_instance,
35527                               filename);
35528       return *this;
35529     }
35530 
35531     // Get a 40x38 color logo of a 'danger' item (internal).
35532     static CImg<T> logo40x38() {
35533       static bool first_time = true;
35534       static CImg<T> res(40,38,1,3);
35535       if (first_time) {
35536         const unsigned char *ptrs = cimg::logo40x38;
35537         T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
35538         for (unsigned int off = 0; off<res._width*res._height;) {
35539           const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
35540           for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
35541         }
35542         first_time = false;
35543       }
35544       return res;
35545     }
35546 
35547     //@}
35548   };
35549 
35550   /*
35551    #-----------------------------------------
35552    #
35553    #
35554    #
35555    # Definition of the CImgList<T> structure
35556    #
35557    #
35558    #
35559    #------------------------------------------
35560    */
35561 
35562   //! Class representing list of images CImg<T>.
35563   template<typename T>
35564   struct CImgList {
35565 
35566     //! Size of the list (number of images).
35567     unsigned int _width;
35568 
35569     //! Allocation size of the list.
35570     unsigned int _allocated_width;
35571 
35572     //! Pointer to the first list element.
35573     CImg<T> *_data;
35574 
35575     //! Define a CImgList<T>::iterator.
35576     typedef CImg<T>* iterator;
35577 
35578     //! Define a CImgList<T>::const_iterator.
35579     typedef const CImg<T>* const_iterator;
35580 
35581     //! Value type.
35582     typedef T value_type;
35583 
35584     // Define common T-dependant types.
35585     typedef typename cimg::superset<T,bool>::type Tbool;
35586     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
35587     typedef typename cimg::superset<T,char>::type Tchar;
35588     typedef typename cimg::superset<T,unsigned short>::type Tushort;
35589     typedef typename cimg::superset<T,short>::type Tshort;
35590     typedef typename cimg::superset<T,unsigned int>::type Tuint;
35591     typedef typename cimg::superset<T,int>::type Tint;
35592     typedef typename cimg::superset<T,unsigned long>::type Tulong;
35593     typedef typename cimg::superset<T,long>::type Tlong;
35594     typedef typename cimg::superset<T,float>::type Tfloat;
35595     typedef typename cimg::superset<T,double>::type Tdouble;
35596     typedef typename cimg::last<T,bool>::type boolT;
35597     typedef typename cimg::last<T,unsigned char>::type ucharT;
35598     typedef typename cimg::last<T,char>::type charT;
35599     typedef typename cimg::last<T,unsigned short>::type ushortT;
35600     typedef typename cimg::last<T,short>::type shortT;
35601     typedef typename cimg::last<T,unsigned int>::type uintT;
35602     typedef typename cimg::last<T,int>::type intT;
35603     typedef typename cimg::last<T,unsigned long>::type ulongT;
35604     typedef typename cimg::last<T,long>::type longT;
35605     typedef typename cimg::last<T,float>::type floatT;
35606     typedef typename cimg::last<T,double>::type doubleT;
35607 
35608     //@}
35609     //---------------------------
35610     //
35611     //! \name Plugins
35612     //@{
35613     //---------------------------
35614 #ifdef cimglist_plugin
35615 #include cimglist_plugin
35616 #endif
35617 #ifdef cimglist_plugin1
35618 #include cimglist_plugin1
35619 #endif
35620 #ifdef cimglist_plugin2
35621 #include cimglist_plugin2
35622 #endif
35623 #ifdef cimglist_plugin3
35624 #include cimglist_plugin3
35625 #endif
35626 #ifdef cimglist_plugin4
35627 #include cimglist_plugin4
35628 #endif
35629 #ifdef cimglist_plugin5
35630 #include cimglist_plugin5
35631 #endif
35632 #ifdef cimglist_plugin6
35633 #include cimglist_plugin6
35634 #endif
35635 #ifdef cimglist_plugin7
35636 #include cimglist_plugin7
35637 #endif
35638 #ifdef cimglist_plugin8
35639 #include cimglist_plugin8
35640 #endif
35641 
35642     //@}
35643     //--------------------------------------------------------
35644     //
35645     //! \name Constructors / Destructor / Instance Management
35646     //@{
35647     //--------------------------------------------------------
35648 
35649     //! Destructor.
35650     ~CImgList() {
35651       if (_data) delete[] _data;
35652     }
35653 
35654     //! Default constructor.
35655     CImgList():
35656       _width(0),_allocated_width(0),_data(0) {}
35657 
35658     //! Construct an image list containing n empty images.
35659     explicit CImgList(const unsigned int n):_width(n) {
35660       if (n) _data = new CImg<T>[_allocated_width = cimg::max(16UL,cimg::nearest_pow2(n))];
35661       else { _allocated_width = 0; _data = 0; }
35662     }
35663 
35664     //! Construct an image list containing n images with specified size.
35665     CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
35666              const unsigned int depth=1, const unsigned int spectrum=1):
35667       _width(0),_allocated_width(0),_data(0) {
35668       assign(n);
35669       cimglist_apply(*this,assign)(width,height,depth,spectrum);
35670     }
35671 
35672     //! Construct an image list containing n images with specified size, filled with specified value.
35673     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
35674              const unsigned int depth, const unsigned int spectrum, const T val):
35675       _width(0),_allocated_width(0),_data(0) {
35676       assign(n);
35677       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
35678     }
35679 
35680     //! Construct an image list containing n images with specified size and specified pixel values (int version).
35681     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
35682              const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
35683       _width(0),_allocated_width(0),_data(0) {
35684 #define _CImgList_stdarg(t) { \
35685         assign(n,width,height,depth,spectrum); \
35686         const unsigned int siz = width*height*depth*spectrum, nsiz = siz*n; \
35687         T *ptrd = _data->_data; \
35688         va_list ap; \
35689         va_start(ap,val1); \
35690         for (unsigned int l = 0, s = 0, i = 0; i<nsiz; ++i) { \
35691           *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
35692           if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
35693         } \
35694         va_end(ap); \
35695       }
35696       _CImgList_stdarg(int);
35697     }
35698 
35699     //! Construct an image list containing n images with specified size and specified pixel values (double version).
35700     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
35701              const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
35702       _width(0),_allocated_width(0),_data(0) {
35703       _CImgList_stdarg(double);
35704     }
35705 
35706     //! Construct a list containing n copies of the image img.
35707     template<typename t>
35708     CImgList(const unsigned int n, const CImg<t>& img, const bool shared=false):
35709       _width(0),_allocated_width(0),_data(0) {
35710       assign(n);
35711       cimglist_apply(*this,assign)(img,shared);
35712     }
35713 
35714     //! Construct an image list from one image.
35715     template<typename t>
35716     explicit CImgList(const CImg<t>& img, const bool shared=false):
35717       _width(0),_allocated_width(0),_data(0) {
35718       assign(1);
35719       _data[0].assign(img,shared);
35720     }
35721 
35722     //! Construct an image list from two images.
35723     template<typename t1, typename t2>
35724     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool shared=false):
35725       _width(0),_allocated_width(0),_data(0) {
35726       assign(2);
35727       _data[0].assign(img1,shared); _data[1].assign(img2,shared);
35728     }
35729 
35730     //! Construct an image list from three images.
35731     template<typename t1, typename t2, typename t3>
35732     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool shared=false):
35733       _width(0),_allocated_width(0),_data(0) {
35734       assign(3);
35735       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared);
35736     }
35737 
35738     //! Construct an image list from four images.
35739     template<typename t1, typename t2, typename t3, typename t4>
35740     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, const bool shared=false):
35741       _width(0),_allocated_width(0),_data(0) {
35742       assign(4);
35743       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35744     }
35745 
35746     //! Construct an image list from five images.
35747     template<typename t1, typename t2, typename t3, typename t4, typename t5>
35748     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
35749              const CImg<t5>& img5, const bool shared=false):
35750       _width(0),_allocated_width(0),_data(0) {
35751       assign(5);
35752       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35753       _data[4].assign(img5,shared);
35754     }
35755 
35756     //! Construct an image list from six images.
35757     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
35758     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
35759              const CImg<t5>& img5, const CImg<t6>& img6, const bool shared=false):
35760       _width(0),_allocated_width(0),_data(0) {
35761       assign(6);
35762       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35763       _data[4].assign(img5,shared); _data[5].assign(img6,shared);
35764     }
35765 
35766     //! Construct an image list from seven images.
35767     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
35768     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
35769              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool shared=false):
35770       _width(0),_allocated_width(0),_data(0) {
35771       assign(7);
35772       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35773       _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared);
35774     }
35775 
35776     //! Construct an image list from eight images.
35777     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
35778     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
35779              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8, const bool shared=false):
35780       _width(0),_allocated_width(0),_data(0) {
35781       assign(8);
35782       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35783       _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared); _data[7].assign(img8,shared);
35784     }
35785 
35786     //! Default copy constructor.
35787     template<typename t>
35788     CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
35789       assign(list._width);
35790       cimglist_for(*this,l) _data[l].assign(list[l],false);
35791     }
35792 
35793     CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
35794       assign(list._width);
35795       cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
35796     }
35797 
35798     //! Advanced copy constructor.
35799     template<typename t>
35800     CImgList(const CImgList<t>& list, const bool shared):_width(0),_allocated_width(0),_data(0) {
35801       assign(list._width);
35802       cimglist_for(*this,l) _data[l].assign(list[l],shared);
35803     }
35804 
35805     //! Construct an image list from a filename.
35806     explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
35807       assign(filename);
35808     }
35809 
35810     //! Construct an image list from a display.
35811     explicit CImgList(const CImgDisplay &disp):_width(0),_allocated_width(0),_data(0) {
35812       assign(disp);
35813     }
35814 
35815     //! Return a shared instance of the list.
35816     CImgList<T> get_shared() {
35817       CImgList<T> res(_width);
35818       cimglist_for(*this,l) res[l].assign(_data[l],true);
35819       return res;
35820     }
35821 
35822     //! Return a shared instance of the list.
35823     const CImgList<T> get_shared() const {
35824       CImgList<T> res(_width);
35825       cimglist_for(*this,l) res[l].assign(_data[l],true);
35826       return res;
35827     }
35828 
35829     //! In-place version of the default constructor.
35830     /**
35831        This function is strictly equivalent to \ref assign() and has been
35832        introduced for having a STL-compliant function name.
35833     **/
35834     CImgList<T>& clear() {
35835       return assign();
35836     }
35837 
35838     //! In-place version of the default constructor and default destructor.
35839     CImgList<T>& assign() {
35840       if (_data) delete[] _data;
35841       _width = _allocated_width = 0;
35842       _data = 0;
35843       return *this;
35844     }
35845 
35846     //! In-place version of the corresponding constructor.
35847     CImgList<T>& assign(const unsigned int n) {
35848       if (n) {
35849         if (_allocated_width<n || _allocated_width>(n<<2)) {
35850           if (_data) delete[] _data;
35851           _data = new CImg<T>[_allocated_width=cimg::max(16UL,cimg::nearest_pow2(n))];
35852         }
35853         _width = n;
35854       } else assign();
35855       return *this;
35856     }
35857 
35858     //! In-place version of the corresponding constructor.
35859     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
35860                         const unsigned int depth=1, const unsigned int spectrum=1) {
35861       assign(n);
35862       cimglist_apply(*this,assign)(width,height,depth,spectrum);
35863       return *this;
35864     }
35865 
35866     //! In-place version of the corresponding constructor.
35867     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
35868                         const unsigned int depth, const unsigned int spectrum, const T val) {
35869       assign(n);
35870       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
35871       return *this;
35872     }
35873 
35874     //! In-place version of the corresponding constructor.
35875     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
35876                         const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
35877       _CImgList_stdarg(int);
35878       return *this;
35879     }
35880 
35881     //! In-place version of the corresponding constructor.
35882     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
35883                         const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...) {
35884       _CImgList_stdarg(double);
35885       return *this;
35886     }
35887 
35888     //! In-place version of the copy constructor.
35889     template<typename t>
35890     CImgList<T>& assign(const CImgList<t>& list, const bool shared=false) {
35891       assign(list._width);
35892       cimglist_for(*this,l) _data[l].assign(list[l],shared);
35893       return *this;
35894     }
35895 
35896     //! In-place version of the corresponding constructor.
35897     template<typename t>
35898     CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool shared=false) {
35899       assign(n);
35900       cimglist_apply(*this,assign)(img,shared);
35901       return *this;
35902     }
35903 
35904     //! In-place version of the corresponding constructor.
35905     template<typename t>
35906     CImgList<T>& assign(const CImg<t>& img, const bool shared=false) {
35907       assign(1);
35908       _data[0].assign(img,shared);
35909       return *this;
35910     }
35911 
35912     //! In-place version of the corresponding constructor.
35913     template<typename t1, typename t2>
35914     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool shared=false) {
35915       assign(2);
35916       _data[0].assign(img1,shared); _data[1].assign(img2,shared);
35917       return *this;
35918     }
35919 
35920     //! In-place version of the corresponding constructor.
35921     template<typename t1, typename t2, typename t3>
35922     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool shared=false) {
35923       assign(3);
35924       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared);
35925       return *this;
35926     }
35927 
35928     //! In-place version of the corresponding constructor.
35929     template<typename t1, typename t2, typename t3, typename t4>
35930     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
35931                         const bool shared=false) {
35932       assign(4);
35933       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35934       return *this;
35935     }
35936 
35937     //! In-place version of the corresponding constructor.
35938     template<typename t1, typename t2, typename t3, typename t4, typename t5>
35939     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
35940                         const CImg<t5>& img5, const bool shared=false) {
35941       assign(5);
35942       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35943       _data[4].assign(img5,shared);
35944       return *this;
35945     }
35946 
35947     //! In-place version of the corresponding constructor.
35948     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
35949     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
35950                         const CImg<t5>& img5, const CImg<t6>& img6, const bool shared=false) {
35951       assign(6);
35952       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35953       _data[4].assign(img5,shared); _data[5].assign(img6,shared);
35954       return *this;
35955     }
35956 
35957     //! In-place version of the corresponding constructor.
35958     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
35959     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
35960                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool shared=false) {
35961       assign(7);
35962       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35963       _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared);
35964       return *this;
35965     }
35966 
35967     //! In-place version of the corresponding constructor.
35968     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
35969     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
35970                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
35971                         const bool shared=false) {
35972       assign(8);
35973       _data[0].assign(img1,shared); _data[1].assign(img2,shared); _data[2].assign(img3,shared); _data[3].assign(img4,shared);
35974       _data[4].assign(img5,shared); _data[5].assign(img6,shared); _data[6].assign(img7,shared); _data[7].assign(img8,shared);
35975       return *this;
35976     }
35977 
35978     //! In-place version of the corresponding constructor.
35979     CImgList<T>& assign(const char *const filename) {
35980       return load(filename);
35981     }
35982 
35983     //! In-place version of the corresponding constructor.
35984     CImgList<T>& assign(const CImgDisplay &disp) {
35985       return assign(CImg<T>(disp));
35986     }
35987 
35988     //! Move the content of the instance image list into another one.
35989     template<typename t>
35990     CImgList<T>& move_to(CImgList<t>& list) {
35991       list.assign(size());
35992       cimglist_for(*this,l) _data[l].move_to(list[l]);
35993       assign();
35994       return list;
35995     }
35996 
35997     CImgList<T>& move_to(CImgList<T>& list) {
35998       list.assign();
35999       return swap(list);
36000     }
36001 
36002     template<typename t>
36003     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
36004       if (is_empty()) return list;
36005       const unsigned int npos = pos>list._width?list._width:pos;
36006       list.insert(_width,npos);
36007       cimglist_for(*this,l) (*this)[l].move_to(list.at(npos+l));
36008       assign();
36009       return list;
36010     }
36011 
36012     //! Swap all fields of two CImgList instances (use with care !)
36013     CImgList<T>& swap(CImgList<T>& list) {
36014       cimg::swap(_width,list._width);
36015       cimg::swap(_allocated_width,list._allocated_width);
36016       cimg::swap(_data,list._data);
36017       return list;
36018     }
36019 
36020     //! Return a reference to an empty list.
36021     static CImgList<T>& empty() {
36022       static CImgList<T> _empty;
36023       return _empty.assign();
36024     }
36025 
36026     //@}
36027     //------------------------------------------
36028     //
36029     //! \name Overloaded Operators
36030     //@{
36031     //------------------------------------------
36032 
36033     //! Return a reference to the i-th element of the image list.
36034     CImg<T>& operator()(const unsigned int pos) {
36035 #if cimg_verbosity>=3
36036       if (pos>=_width) {
36037         cimg::warn(_cimglist_instance
36038                    "operator() : Invalid image request, at position [%u].",
36039                    cimglist_instance,
36040                    pos);
36041         return *_data;
36042       }
36043 #endif
36044       return _data[pos];
36045     }
36046 
36047     const CImg<T>& operator()(const unsigned int pos) const {
36048       return const_cast<CImgList<T>*>(this)->operator()(pos);
36049     }
36050 
36051     //! Return a reference to (x,y,z,c) pixel of the pos-th image of the list
36052     T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
36053                   const unsigned int z=0, const unsigned int c=0) {
36054       return (*this)[pos](x,y,z,c);
36055     }
36056     const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
36057                         const unsigned int z=0, const unsigned int c=0) const {
36058       return (*this)[pos](x,y,z,c);
36059     }
36060 
36061     //! Return address of the image vector.
36062     operator const CImg<T>*() const {
36063       return _data;
36064     }
36065 
36066     operator CImg<T>*() {
36067       return _data;
36068     }
36069 
36070     //! Operator=().
36071     template<typename t>
36072     CImgList<T>& operator=(const CImg<t>& img) {
36073       return assign(img);
36074     }
36075 
36076     //! Operator=().
36077     CImgList<T>& operator=(const CImgDisplay& disp) {
36078       return assign(disp);
36079     }
36080 
36081     //! Operator=().
36082     template<typename t>
36083     CImgList<T>& operator=(const CImgList<t>& list) {
36084       return assign(list);
36085     }
36086 
36087     CImgList<T>& operator=(const CImgList<T>& list) {
36088       return assign(list);
36089     }
36090 
36091     //! Operator=().
36092     CImgList<T>& operator=(const char *const filename) {
36093       return assign(filename);
36094     }
36095 
36096     //! Operator+() (unary).
36097     /**
36098        Writting '+list' is a convenient shortcut to 'CImgList<T>(list,false)'
36099        (forces a copy with non-shared elements).
36100      **/
36101     CImgList<T> operator+() const {
36102       return CImgList<T>(*this,false);
36103     }
36104 
36105     //! Operator,().
36106     template<typename t>
36107     CImgList<T>& operator,(const CImg<t>& img) {
36108       return insert(img);
36109     }
36110 
36111     //! Operator,().
36112     template<typename t>
36113     CImgList<T>& operator,(const CImgList<t>& list) {
36114       return insert(list);
36115     }
36116 
36117     //! Operator>().
36118     CImg<T> operator>(const char axis) const {
36119       return get_append(axis,'p');
36120     }
36121 
36122     //! Operator<().
36123     CImgList<T> operator<(const char axis) const {
36124       return get_split(axis);
36125     }
36126 
36127     //@}
36128     //-------------------------------------
36129     //
36130     //! \name Instance Characteristics
36131     //@{
36132     //-------------------------------------
36133 
36134     //! Return a string describing the type of the image pixels in the list (template parameter \p T).
36135     static const char* pixel_type() {
36136       return cimg::type<T>::string();
36137     }
36138 
36139     //! Return the size of the list.
36140     int width() const {
36141       return (int)_width;
36142     }
36143 
36144     //! Return the size of the list.
36145     unsigned int size() const {
36146       return _width;
36147     }
36148 
36149     //! Return a pointer to the image buffer.
36150     CImg<T> *data() {
36151       return _data;
36152     }
36153 
36154     const CImg<T> *data() const {
36155       return _data;
36156     }
36157 
36158     //! Return a pointer to the image buffer.
36159 #if cimg_verbosity>=3
36160     CImg<T> *data(const unsigned int l) {
36161       if (l>=size()) {
36162         cimg::warn(_cimglist_instance
36163                    "data() : Invalid pointer request, at position [%u].",
36164                    cimglist_instance,
36165                    l);
36166         return _data;
36167       }
36168       return _data + l;
36169     }
36170 
36171     const CImg<T> *data(const unsigned int l) const {
36172       return const_cast<CImgList<T>*>(this)->data(l);
36173     }
36174 #else
36175     CImg<T> *data(const unsigned int l) {
36176       return _data + l;
36177     }
36178 
36179     const CImg<T> *data(const unsigned int l) const {
36180       return _data + l;
36181     }
36182 #endif
36183 
36184     //! Returns an iterator to the beginning of the vector (STL-compliant name).
36185     iterator begin() {
36186       return _data;
36187     }
36188 
36189     const_iterator begin() const {
36190       return _data;
36191     }
36192 
36193     //! Returns an iterator just past the last element (STL-compliant name).
36194     iterator end() {
36195       return _data + _width;
36196     }
36197 
36198     const_iterator end() const {
36199       return _data + _width;
36200     }
36201 
36202     //! Returns a reference to the first element (STL-compliant name).
36203     CImg<T>& front() {
36204       return *_data;
36205     }
36206 
36207     const CImg<T>& front() const {
36208       return *_data;
36209     }
36210 
36211     //! Return a reference to the last image (STL-compliant name).
36212     const CImg<T>& back() const {
36213       return *(_data + _width - 1);
36214     }
36215 
36216     CImg<T>& back() {
36217       return *(_data + _width - 1);
36218     }
36219 
36220     //! Read an image in specified position.
36221     CImg<T>& at(const int pos) {
36222       if (is_empty())
36223         throw CImgInstanceException(_cimglist_instance
36224                                     "at() : Empty instance.",
36225                                     cimglist_instance);
36226 
36227       return _data[pos<0?0:pos>=(int)_width?(int)_width-1:pos];
36228     }
36229 
36230     //! Read a pixel value with Dirichlet boundary conditions.
36231     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_val) {
36232       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_val)=out_val):_data[pos].atXYZC(x,y,z,c,out_val);
36233     }
36234 
36235     T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_val) const {
36236       return (pos<0 || pos>=(int)_width)?out_val:_data[pos].atXYZC(x,y,z,c,out_val);
36237     }
36238 
36239     //! Read a pixel value with Neumann boundary conditions.
36240     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
36241       if (is_empty())
36242         throw CImgInstanceException(_cimglist_instance
36243                                     "atNXYZC() : Empty instance.",
36244                                     cimglist_instance);
36245 
36246       return _atNXYZC(pos,x,y,z,c);
36247     }
36248 
36249     T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
36250       if (is_empty())
36251         throw CImgInstanceException(_cimglist_instance
36252                                     "atNXYZC() : Empty instance.",
36253                                     cimglist_instance);
36254 
36255       return _atNXYZC(pos,x,y,z,c);
36256     }
36257 
36258     T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
36259       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
36260     }
36261 
36262     T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
36263       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
36264     }
36265 
36266     //! Read a pixel value with Dirichlet boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z).
36267     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_val) {
36268       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_val)=out_val):_data[pos].atXYZ(x,y,z,c,out_val);
36269     }
36270 
36271     T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_val) const {
36272       return (pos<0 || pos>=(int)_width)?out_val:_data[pos].atXYZ(x,y,z,c,out_val);
36273     }
36274 
36275     //! Read a pixel value with Neumann boundary conditions for the four first coordinates (\c pos, \c x,\c y,\c z).
36276     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
36277       if (is_empty())
36278         throw CImgInstanceException(_cimglist_instance
36279                                     "atNXYZ() : Empty instance.",
36280                                     cimglist_instance);
36281 
36282       return _atNXYZ(pos,x,y,z,c);
36283     }
36284 
36285     T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
36286       if (is_empty())
36287         throw CImgInstanceException(_cimglist_instance
36288                                     "atNXYZ() : Empty instance.",
36289                                     cimglist_instance);
36290 
36291       return _atNXYZ(pos,x,y,z,c);
36292     }
36293 
36294     T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
36295       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
36296     }
36297 
36298     T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
36299       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
36300     }
36301 
36302     //! Read a pixel value with Dirichlet boundary conditions for the three first coordinates (\c pos, \c x,\c y).
36303     T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_val) {
36304       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_val)=out_val):_data[pos].atXY(x,y,z,c,out_val);
36305     }
36306 
36307     T atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_val) const {
36308       return (pos<0 || pos>=(int)_width)?out_val:_data[pos].atXY(x,y,z,c,out_val);
36309     }
36310 
36311     //! Read a pixel value with Neumann boundary conditions for the three first coordinates (\c pos, \c x,\c y).
36312     T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
36313       if (is_empty())
36314         throw CImgInstanceException(_cimglist_instance
36315                                     "atNXY() : Empty instance.",
36316                                     cimglist_instance);
36317 
36318       return _atNXY(pos,x,y,z,c);
36319     }
36320 
36321     T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
36322       if (is_empty())
36323         throw CImgInstanceException(_cimglist_instance
36324                                     "atNXY() : Empty instance.",
36325                                     cimglist_instance);
36326 
36327       return _atNXY(pos,x,y,z,c);
36328     }
36329 
36330     T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
36331       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
36332     }
36333 
36334     T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
36335       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
36336     }
36337 
36338     //! Read a pixel value with Dirichlet boundary conditions for the two first coordinates (\c pos,\c x).
36339     T& atNX(const int pos, const int x, const int y, const int z, const int c, const T out_val) {
36340       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_val)=out_val):_data[pos].atX(x,y,z,c,out_val);
36341     }
36342 
36343     T atNX(const int pos, const int x, const int y, const int z, const int c, const T out_val) const {
36344       return (pos<0 || pos>=(int)_width)?out_val:_data[pos].atX(x,y,z,c,out_val);
36345     }
36346 
36347     //! Read a pixel value with Neumann boundary conditions for the two first coordinates (\c pos, \c x).
36348     T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
36349       if (is_empty())
36350         throw CImgInstanceException(_cimglist_instance
36351                                     "atNX() : Empty instance.",
36352                                     cimglist_instance);
36353 
36354       return _atNX(pos,x,y,z,c);
36355     }
36356 
36357     T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
36358       if (is_empty())
36359         throw CImgInstanceException(_cimglist_instance
36360                                     "atNX() : Empty instance.",
36361                                     cimglist_instance);
36362 
36363       return _atNX(pos,x,y,z,c);
36364     }
36365 
36366     T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
36367       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
36368     }
36369 
36370     T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
36371       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
36372     }
36373 
36374     //! Read a pixel value with Dirichlet boundary conditions for the first coordinates (\c pos).
36375     T& atN(const int pos, const int x, const int y, const int z, const int c, const T out_val) {
36376       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_val)=out_val):(*this)(pos,x,y,z,c);
36377     }
36378 
36379     T atN(const int pos, const int x, const int y, const int z, const int c, const T out_val) const {
36380       return (pos<0 || pos>=(int)_width)?out_val:(*this)(pos,x,y,z,c);
36381     }
36382 
36383     //! Read a pixel value with Neumann boundary conditions for the first coordinates (\c pos).
36384     T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
36385       if (is_empty())
36386         throw CImgInstanceException(_cimglist_instance
36387                                     "atN() : Empty instance.",
36388                                     cimglist_instance);
36389       return _atN(pos,x,y,z,c);
36390     }
36391 
36392     T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
36393       if (is_empty())
36394         throw CImgInstanceException(_cimglist_instance
36395                                     "atN() : Empty instance.",
36396                                     cimglist_instance);
36397       return _atN(pos,x,y,z,c);
36398     }
36399 
36400     T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
36401       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
36402     }
36403 
36404     T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
36405       return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
36406     }
36407 
36408     //! Return a C-string containing the values of all images in the instance list.
36409     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
36410       if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
36411       CImgList<charT> items;
36412       for (unsigned int l = 0; l<_width-1; ++l) {
36413         CImg<charT> item = _data[l].value_string(separator,0);
36414         item.back() = separator;
36415         item.move_to(items);
36416       }
36417       _data[_width-1].value_string(separator,0).move_to(items);
36418       CImg<charT> res; (items>'x').move_to(res);
36419       if (max_size) { res.crop(0,max_size); res(max_size) = 0; }
36420       return res;
36421     }
36422 
36423     //@}
36424     //-------------------------------------
36425     //
36426     //! \name Instance Checking
36427     //@{
36428     //-------------------------------------
36429 
36430     //! Return \p true if list is empty.
36431     bool is_empty() const {
36432       return (!_data || !_width);
36433     }
36434 
36435     //! Return \p true if list if of specified size.
36436     bool is_sameN(const unsigned int n) const {
36437       return (_width==n);
36438     }
36439 
36440     //! Return \p true if list if of specified size.
36441     template<typename t>
36442     bool is_sameN(const CImgList<t>& list) const {
36443       return (_width==list._width);
36444     }
36445 
36446     // Define useful dimension check functions.
36447     // (not documented because they are macro-generated).
36448 #define _cimglist_def_is_same1(axis) \
36449     bool is_same##axis(const unsigned int val) const { \
36450       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); return res; \
36451     } \
36452     bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
36453       return is_sameN(n) && is_same##axis(val); \
36454     } \
36455 
36456 #define _cimglist_def_is_same2(axis1,axis2) \
36457     bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
36458       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); return res; \
36459     } \
36460     bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
36461       return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
36462     } \
36463 
36464 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
36465     bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
36466       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); return res; \
36467     } \
36468     bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
36469       return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
36470     } \
36471 
36472 #define _cimglist_def_is_same(axis) \
36473     template<typename t> bool is_same##axis(const CImg<t>& img) const { \
36474       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \
36475     } \
36476     template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
36477       const unsigned int lmin = cimg::min(_width,list._width); \
36478       bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); return res; \
36479     } \
36480     template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
36481       return (is_sameN(n) && is_same##axis(img)); \
36482     } \
36483     template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
36484       return (is_sameN(list) && is_same##axis(list)); \
36485     }
36486 
36487     _cimglist_def_is_same(XY)
36488     _cimglist_def_is_same(XZ)
36489     _cimglist_def_is_same(XC)
36490     _cimglist_def_is_same(YZ)
36491     _cimglist_def_is_same(YC)
36492     _cimglist_def_is_same(XYZ)
36493     _cimglist_def_is_same(XYC)
36494     _cimglist_def_is_same(YZC)
36495     _cimglist_def_is_same(XYZC)
36496     _cimglist_def_is_same1(X)
36497     _cimglist_def_is_same1(Y)
36498     _cimglist_def_is_same1(Z)
36499     _cimglist_def_is_same1(C)
36500     _cimglist_def_is_same2(X,Y)
36501     _cimglist_def_is_same2(X,Z)
36502     _cimglist_def_is_same2(X,C)
36503     _cimglist_def_is_same2(Y,Z)
36504     _cimglist_def_is_same2(Y,C)
36505     _cimglist_def_is_same2(Z,C)
36506     _cimglist_def_is_same3(X,Y,Z)
36507     _cimglist_def_is_same3(X,Y,C)
36508     _cimglist_def_is_same3(X,Z,C)
36509     _cimglist_def_is_same3(Y,Z,C)
36510 
36511     bool is_sameXYZC(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
36512       bool res = true;
36513       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
36514       return res;
36515     }
36516 
36517     bool is_sameNXYZC(const unsigned int n, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
36518       return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
36519     }
36520 
36521     //! Return \c true if the list contains the pixel (n,x,y,z,c).
36522     bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
36523       if (is_empty()) return false;
36524       return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
36525         z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
36526     }
36527 
36528     //! Return \c true if the list contains the image (n).
36529     bool containsN(const int n) const {
36530       if (is_empty()) return false;
36531       return n>=0 && n<(int)_width;
36532     }
36533 
36534     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z,c).
36535     template<typename t>
36536     bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
36537       if (is_empty()) return false;
36538       cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
36539       return false;
36540     }
36541 
36542     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y,z).
36543     template<typename t>
36544     bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
36545       t c;
36546       return contains(pixel,n,x,y,z,c);
36547     }
36548 
36549     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x,y).
36550     template<typename t>
36551     bool contains(const T& pixel, t& n, t& x, t&y) const {
36552       t z, c;
36553       return contains(pixel,n,x,y,z,c);
36554     }
36555 
36556     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n,x).
36557     template<typename t>
36558     bool contains(const T& pixel, t& n, t& x) const {
36559       t y, z, c;
36560       return contains(pixel,n,x,y,z,c);
36561     }
36562 
36563     //! Return \c true if one of the image list contains the specified referenced value. If true, set coordinates (n).
36564     template<typename t>
36565     bool contains(const T& pixel, t& n) const {
36566       t x, y, z, c;
36567       return contains(pixel,n,x,y,z,c);
36568     }
36569 
36570     //! Return \c true if one of the image list contains the specified referenced value.
36571     bool contains(const T& pixel) const {
36572       unsigned int n, x, y, z, c;
36573       return contains(pixel,n,x,y,z,c);
36574     }
36575 
36576     //! Return \c true if the list contains the image 'img'. If true, returns the position (n) of the image in the list.
36577     template<typename t>
36578     bool contains(const CImg<T>& img, t& n) const {
36579       if (is_empty()) return false;
36580       const CImg<T> *const ptr = &img;
36581       cimglist_for(*this,i) if (_data+i==ptr) { n = (t)i; return true; }
36582       return false;
36583     }
36584 
36585     //! Return \c true if the list contains the image img.
36586     bool contains(const CImg<T>& img) const {
36587       unsigned int n;
36588       return contains(img,n);
36589     }
36590 
36591     //@}
36592     //-------------------------------------
36593     //
36594     //! \name Mathematical Functions
36595     //@{
36596     //-------------------------------------
36597 
36598     //! Return a reference to the minimum pixel value of the instance list.
36599     T& min() {
36600       if (is_empty())
36601         throw CImgInstanceException(_cimglist_instance
36602                                     "min() : Empty instance.",
36603                                     cimglist_instance);
36604       T *ptr_min = _data->_data;
36605       T min_value = *ptr_min;
36606       cimglist_for(*this,l) {
36607         const CImg<T>& img = _data[l];
36608         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
36609       }
36610       return *ptr_min;
36611     }
36612 
36613     const T& min() const {
36614       if (is_empty())
36615         throw CImgInstanceException(_cimglist_instance
36616                                     "min() : Empty instance.",
36617                                     cimglist_instance);
36618       const T *ptr_min = _data->_data;
36619       T min_value = *ptr_min;
36620       cimglist_for(*this,l) {
36621         const CImg<T>& img = _data[l];
36622         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
36623       }
36624       return *ptr_min;
36625     }
36626 
36627     //! Return a reference to the maximum pixel value of the instance list.
36628     T& max() {
36629       if (is_empty())
36630         throw CImgInstanceException(_cimglist_instance
36631                                     "max() : Empty instance.",
36632                                     cimglist_instance);
36633       T *ptr_max = _data->_data;
36634       T max_value = *ptr_max;
36635       cimglist_for(*this,l) {
36636         const CImg<T>& img = _data[l];
36637         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
36638       }
36639       return *ptr_max;
36640     }
36641 
36642     const T& max() const {
36643       if (is_empty())
36644         throw CImgInstanceException(_cimglist_instance
36645                                     "max() : Empty instance.",
36646                                     cimglist_instance);
36647       const T *ptr_max = _data->_data;
36648       T max_value = *ptr_max;
36649       cimglist_for(*this,l) {
36650         const CImg<T>& img = _data[l];
36651         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
36652       }
36653       return *ptr_max;
36654     }
36655 
36656     //! Return a reference to the minimum pixel value of the instance list.
36657     template<typename t>
36658     T& min_max(t& max_val) {
36659       if (is_empty())
36660         throw CImgInstanceException(_cimglist_instance
36661                                     "min_max() : Empty instance.",
36662                                     cimglist_instance);
36663       T *ptr_min = _data->_data;
36664       T min_value = *ptr_min, max_value = min_value;
36665       cimglist_for(*this,l) {
36666         const CImg<T>& img = _data[l];
36667         cimg_for(img,ptrs,T) {
36668           const T val = *ptrs;
36669           if (val<min_value) { min_value = val; ptr_min = ptrs; }
36670           if (val>max_value) max_value = val;
36671         }
36672       }
36673       max_val = (t)max_value;
36674       return *ptr_min;
36675     }
36676 
36677     template<typename t>
36678     const T& min_max(t& max_val) const {
36679       if (is_empty())
36680         throw CImgInstanceException(_cimglist_instance
36681                                     "min_max() : Empty instance.",
36682                                     cimglist_instance);
36683       const T *ptr_min = _data->_data;
36684       T min_value = *ptr_min, max_value = min_value;
36685       cimglist_for(*this,l) {
36686         const CImg<T>& img = _data[l];
36687         cimg_for(img,ptrs,T) {
36688           const T val = *ptrs;
36689           if (val<min_value) { min_value = val; ptr_min = ptrs; }
36690           if (val>max_value) max_value = val;
36691         }
36692       }
36693       max_val = (t)max_value;
36694       return *ptr_min;
36695     }
36696 
36697     //! Return a reference to the minimum pixel value of the instance list.
36698     template<typename t>
36699     T& max_min(t& min_val) {
36700       if (is_empty())
36701         throw CImgInstanceException(_cimglist_instance
36702                                     "max_min() : Empty instance.",
36703                                     cimglist_instance);
36704       T *ptr_max = _data->_data;
36705       T min_value = *ptr_max, max_value = min_value;
36706       cimglist_for(*this,l) {
36707         const CImg<T>& img = _data[l];
36708         cimg_for(img,ptrs,T) {
36709           const T val = *ptrs;
36710           if (val>max_value) { max_value = val; ptr_max = ptrs; }
36711           if (val<min_value) min_value = val;
36712         }
36713       }
36714       min_val = (t)min_value;
36715       return *ptr_max;
36716     }
36717 
36718     template<typename t>
36719     const T& max_min(t& min_val) const {
36720       if (is_empty())
36721         throw CImgInstanceException(_cimglist_instance
36722                                     "max_min() : Empty instance.",
36723                                     cimglist_instance);
36724       const T *ptr_max = _data->_data;
36725       T min_value = *ptr_max, max_value = min_value;
36726       cimglist_for(*this,l) {
36727         const CImg<T>& img = _data[l];
36728         cimg_for(img,ptrs,T) {
36729           const T val = *ptrs;
36730           if (val>max_value) { max_value = val; ptr_max = ptrs; }
36731           if (val<min_value) min_value = val;
36732         }
36733       }
36734       min_val = (t)min_value;
36735       return *ptr_max;
36736     }
36737 
36738     //@}
36739     //---------------------------
36740     //
36741     //! \name List Manipulation
36742     //@{
36743     //---------------------------
36744 
36745     //! Insert a copy of the image \p img into the current image list, at position \p pos.
36746     template<typename t>
36747     CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool shared=false) {
36748       const unsigned int npos = pos==~0U?_width:pos;
36749       if (npos>_width)
36750         throw CImgArgumentException(_cimglist_instance
36751                                     "insert() : Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.",
36752                                     cimglist_instance,
36753                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
36754       if (shared)
36755         throw CImgArgumentException(_cimglist_instance
36756                                     "insert() : Invalid insertion request of specified shared image CImg<%s>(%u,%u,%u,%u,%p) at position %u "
36757                                     "(pixel types are different).",
36758                                     cimglist_instance,
36759                                     img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
36760 
36761       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0;
36762       if (!_width || !_data) {
36763         _data = new_data;
36764         *_data = img;
36765       } else {
36766         if (new_data) {
36767           if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
36768           if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
36769           std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
36770           delete[] _data;
36771           _data = new_data;
36772         }
36773         else if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
36774         _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0;
36775         _data[npos] = img;
36776       }
36777       return *this;
36778     }
36779 
36780     CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool shared=false) {
36781       const unsigned int npos = pos==~0U?_width:pos;
36782       if (npos>_width)
36783         throw CImgArgumentException(_cimglist_instance
36784                                     "insert() : Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.",
36785                                     cimglist_instance,
36786                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
36787 
36788       //      if (&img>=data() && &img<end()) return insert(+img,pos,shared);
36789 
36790       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0;
36791       if (!_width || !_data) {
36792         _data = new_data;
36793         if (shared && img) {
36794           _data->_width = img._width; _data->_height = img._height; _data->_depth = img._depth; _data->_spectrum = img._spectrum;
36795           _data->_is_shared = true; _data->_data = img._data;
36796         } else *_data = img;
36797       }
36798       else {
36799         if (new_data) {
36800           if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
36801           if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
36802           if (shared && img) {
36803             new_data[npos]._width = img._width; new_data[npos]._height = img._height; new_data[npos]._depth = img._depth;
36804             new_data[npos]._spectrum = img._spectrum; new_data[npos]._is_shared = true; new_data[npos]._data = img._data;
36805           } else {
36806             new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0; new_data[npos]._data = 0;
36807             new_data[npos] = img;
36808           }
36809           std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
36810           delete[] _data;
36811           _data = new_data;
36812         } else {
36813           if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
36814           if (shared && img) {
36815             _data[npos]._width = img._width; _data[npos]._height = img._height; _data[npos]._depth = img._depth; _data[npos]._spectrum = img._spectrum;
36816             _data[npos]._is_shared = true; _data[npos]._data = img._data;
36817           } else {
36818             _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0;
36819             _data[npos] = img;
36820           }
36821         }
36822       }
36823       return *this;
36824     }
36825 
36826     template<typename t>
36827     CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool shared=false) const {
36828       return (+*this).insert(img,pos,shared);
36829     }
36830 
36831     //! Insert n empty images img into the current image list, at position \p pos.
36832     CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
36833       CImg<T> foo;
36834       if (!n) return *this;
36835       const unsigned int npos = pos==~0U?_width:pos;
36836       for (unsigned int i = 0; i<n; ++i) insert(foo,npos+i);
36837       return *this;
36838     }
36839 
36840     CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
36841       return (+*this).insert(n,pos);
36842     }
36843 
36844     //! Insert n copies of the image \p img into the current image list, at position \p pos.
36845     template<typename t>
36846     CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, const bool shared=false) {
36847       if (!n) return *this;
36848       const unsigned int npos = pos==~0U?_width:pos;
36849       insert(img,npos,shared);
36850       for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos+i,shared);
36851       return *this;
36852     }
36853 
36854     template<typename t>
36855     CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, const bool shared=false) const {
36856       return (+*this).insert(n,img,pos,shared);
36857     }
36858 
36859     //! Insert a copy of the image list \p list into the current image list, starting from position \p pos.
36860     template<typename t>
36861     CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool shared=false) {
36862       const unsigned int npos = pos==~0U?_width:pos;
36863       if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos+l,shared);
36864       else insert(CImgList<T>(list),npos,shared);
36865       return *this;
36866     }
36867 
36868     template<typename t>
36869     CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool shared=false) const {
36870       return (+*this).insert(list,pos,shared);
36871     }
36872 
36873     //! Insert n copies of the list \p list at position \p pos of the current list.
36874     template<typename t>
36875     CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, const bool shared=false) {
36876       if (!n) return *this;
36877       const unsigned int npos = pos==~0U?_width:pos;
36878       for (unsigned int i = 0; i<n; ++i) insert(list,npos,shared);
36879       return *this;
36880     }
36881 
36882     template<typename t>
36883     CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, const bool shared=false) const {
36884       return (+*this).insert(n,list,pos,shared);
36885     }
36886 
36887     //! Remove the images from positions \p pos1 to \p pos2.
36888     CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
36889       const unsigned int
36890         npos1 = pos1<pos2?pos1:pos2,
36891         tpos2 = pos1<pos2?pos2:pos1,
36892         npos2 = tpos2<_width?tpos2:_width-1;
36893       if (npos1>=_width)
36894         throw CImgArgumentException(_cimglist_instance
36895                                     "remove() : Invalid remove request at positions %u->%u.",
36896                                     cimglist_instance,
36897                                     npos1,tpos2);
36898       else {
36899         if (tpos2>=_width)
36900           throw CImgArgumentException(_cimglist_instance
36901                                       "remove() : Invalid remove request at positions %u->%u.",
36902                                       cimglist_instance,
36903                                       npos1,tpos2);
36904 
36905         for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
36906         const unsigned int nb = 1 + npos2 - npos1;
36907         if (!(_width-=nb)) return assign();
36908         if (_width>(_allocated_width>>2) || _allocated_width<=8) { // Removing items without reallocation.
36909           if (npos1!=_width) std::memmove(_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width - npos1));
36910           std::memset(_data + _width,0,sizeof(CImg<T>)*nb);
36911         } else { // Removing items with reallocation.
36912           _allocated_width>>=2;
36913           while (_allocated_width>8 && _width<(_allocated_width>>1)) _allocated_width>>=1;
36914           CImg<T> *const new_data = new CImg<T>[_allocated_width];
36915           if (npos1) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos1);
36916           if (npos1!=_width) std::memcpy(new_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width-npos1));
36917           if (_width!=_allocated_width) std::memset(new_data+_width,0,sizeof(_allocated_width - _width));
36918           std::memset(_data,0,sizeof(CImg<T>)*(_width+nb));
36919           delete[] _data;
36920           _data = new_data;
36921         }
36922       }
36923       return *this;
36924     }
36925 
36926     CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
36927       return (+*this).remove(pos1,pos2);
36928     }
36929 
36930     //! Remove the image at position \p pos from the image list.
36931     CImgList<T>& remove(const unsigned int pos) {
36932       return remove(pos,pos);
36933     }
36934 
36935     CImgList<T> get_remove(const unsigned int pos) const {
36936       return (+*this).remove(pos);
36937     }
36938 
36939     //! Remove the last image from the image list.
36940     CImgList<T>& remove() {
36941       return remove(_width-1);
36942     }
36943 
36944     CImgList<T> get_remove() const {
36945       return (+*this).remove();
36946     }
36947 
36948     //! Reverse list order.
36949     CImgList<T>& reverse() {
36950       for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width-1-l]);
36951       return *this;
36952     }
36953 
36954     CImgList<T> get_reverse() const {
36955       return (+*this).reverse();
36956     }
36957 
36958     //! Get a sub-list.
36959     CImgList<T>& images(const unsigned int i0, const unsigned int i1) {
36960       return get_images(i0,i1).move_to(*this);
36961     }
36962 
36963     CImgList<T> get_images(const unsigned int i0, const unsigned int i1) const {
36964       if (i0>i1 || i1>=_width)
36965         throw CImgArgumentException(_cimglist_instance
36966                                     "images() : Specified sub-list indices (%u->%u) are out of bounds.",
36967                                     cimglist_instance,
36968                                     i0,i1);
36969       CImgList<T> res(i1-i0+1);
36970       cimglist_for(res,l) res[l].assign(_data[i0+l]);
36971       return res;
36972     }
36973 
36974     //! Get a shared sub-list.
36975     CImgList<T> get_shared_images(const unsigned int i0, const unsigned int i1) {
36976       if (i0>i1 || i1>=_width)
36977         throw CImgArgumentException(_cimglist_instance
36978                                     "get_shared_images() : Specified sub-list indices (%u->%u) are out of bounds.",
36979                                     cimglist_instance,
36980                                     i0,i1);
36981       CImgList<T> res(i1-i0+1);
36982       cimglist_for(res,l) res[l].assign(_data[i0+l],true);
36983       return res;
36984     }
36985 
36986     const CImgList<T> get_shared_images(const unsigned int i0, const unsigned int i1) const {
36987       if (i0>i1 || i1>=_width)
36988         throw CImgArgumentException(_cimglist_instance
36989                                     "get_shared_images() : Specified sub-list indices (%u->%u) are out of bounds.",
36990                                     cimglist_instance,
36991                                     i0,i1);
36992       CImgList<T> res(i1-i0+1);
36993       cimglist_for(res,l) res[l].assign(_data[i0+l],true);
36994       return res;
36995     }
36996 
36997     //! Return a single image which is the concatenation of all images of the current CImgList instance.
36998     /**
36999        \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'c'.
37000        \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom).
37001        \return A CImg<T> image corresponding to the concatenation is returned.
37002     **/
37003     CImg<T> get_append(const char axis, const char align='p') const {
37004       if (align!='p' && align!='c' && align!='n')
37005         throw CImgArgumentException(_cimglist_instance
37006                                     "get_append() : Invalid alignment parameter '%c' "
37007                                     "(should be { p | c | n }).",
37008                                     cimglist_instance,
37009                                     align);
37010 
37011       if (is_empty()) return CImg<T>();
37012       if (_width==1) return +((*this)[0]);
37013       unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
37014       CImg<T> res;
37015       switch (cimg::uncase(axis)) {
37016       case 'x' : { // Along the X-axis.
37017         cimglist_for(*this,l) {
37018           const CImg<T>& img = (*this)[l];
37019           dx+=img._width; dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum);
37020         }
37021         res.assign(dx,dy,dz,dc,0);
37022         if (res) switch (cimg::uncase(align)) {
37023         case 'p' : {
37024           cimglist_for(*this,l) {
37025             res.draw_image(pos,(*this)[l]);
37026             pos+=(*this)[l]._width;
37027           }
37028         } break;
37029         case 'c' : {
37030           cimglist_for(*this,l) {
37031             res.draw_image(pos,(dy-(*this)[l]._height)/2,(dz-(*this)[l]._depth)/2,(dc-(*this)[l]._spectrum)/2,(*this)[l]);
37032             pos+=(*this)[l]._width;
37033           }
37034         } break;
37035         default : {
37036           cimglist_for(*this,l) {
37037             res.draw_image(pos,dy-(*this)[l]._height,dz-(*this)[l]._depth,dc-(*this)[l]._spectrum,(*this)[l]);
37038             pos+=(*this)[l]._width;
37039           }
37040         }
37041         }
37042       } break;
37043       case 'y' : { // Along the Y-axis.
37044         cimglist_for(*this,l) {
37045           const CImg<T>& img = (*this)[l];
37046           dx = cimg::max(dx,img._width); dy+=img._height; dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum);
37047         }
37048         res.assign(dx,dy,dz,dc,0);
37049         if (res) switch (cimg::uncase(align)) {
37050         case 'p' : {
37051           cimglist_for(*this,l) { res.draw_image(0,pos,(*this)[l]); pos+=(*this)[l]._height; }
37052         } break;
37053         case 'c' : {
37054           cimglist_for(*this,l) {
37055             res.draw_image((dx-(*this)[l]._width)/2,pos,(dz-(*this)[l]._depth)/2,(dc-(*this)[l]._spectrum)/2,(*this)[l]);
37056             pos+=(*this)[l]._height;
37057           }
37058         } break;
37059         default : {
37060           cimglist_for(*this,l) {
37061             res.draw_image(dx-(*this)[l]._width,pos,dz-(*this)[l]._depth,dc-(*this)[l]._spectrum,(*this)[l]);
37062             pos+=(*this)[l]._height;
37063           }
37064         }
37065         }
37066       } break;
37067       case 'z' : { // Along the Z-axis.
37068         cimglist_for(*this,l) {
37069           const CImg<T>& img = (*this)[l];
37070           dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz+=img._depth; dc = cimg::max(dc,img._spectrum);
37071         }
37072         res.assign(dx,dy,dz,dc,0);
37073         if (res) switch (cimg::uncase(align)) {
37074         case 'p' : {
37075           cimglist_for(*this,l) { res.draw_image(0,0,pos,(*this)[l]); pos+=(*this)[l]._depth; }
37076         } break;
37077         case 'c' : {
37078           cimglist_for(*this,l) {
37079             res.draw_image((dx-(*this)[l]._width)/2,(dy-(*this)[l]._height)/2,pos,(dc-(*this)[l]._spectrum)/2,(*this)[l]);
37080             pos+=(*this)[l]._depth;
37081           }
37082         } break;
37083         default : {
37084           cimglist_for(*this,l) {
37085             res.draw_image(dx-(*this)[l]._width,dy-(*this)[l]._height,pos,dc-(*this)[l]._spectrum,(*this)[l]);
37086             pos+=(*this)[l]._depth;
37087           }
37088         }
37089         }
37090       } break;
37091       default : { // Along the C-axis.
37092         cimglist_for(*this,l) {
37093           const CImg<T>& img = (*this)[l];
37094           dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc+=img._spectrum;
37095         }
37096         res.assign(dx,dy,dz,dc,0);
37097         if (res) switch (cimg::uncase(align)) {
37098         case 'p' : {
37099           cimglist_for(*this,l) { res.draw_image(0,0,0,pos,(*this)[l]); pos+=(*this)[l]._spectrum; }
37100         } break;
37101         case 'c' : {
37102           cimglist_for(*this,l) {
37103             res.draw_image((dx-(*this)[l]._width)/2,(dy-(*this)[l]._height)/2,(dz-(*this)[l]._depth)/2,pos,(*this)[l]);
37104             pos+=(*this)[l]._spectrum;
37105           }
37106         } break;
37107         default : {
37108           cimglist_for(*this,l) {
37109             res.draw_image(dx-(*this)[l]._width,dy-(*this)[l]._height,dz-(*this)[l]._depth,pos,(*this)[l]);
37110             pos+=(*this)[l]._spectrum;
37111           }
37112         }
37113         }
37114       }
37115       }
37116       return res;
37117     }
37118 
37119     //! Return a list where each image has been split along the specified axis.
37120     CImgList<T>& split(const char axis, const int nb=0) {
37121       return get_split(axis,nb).move_to(*this);
37122     }
37123 
37124     CImgList<T> get_split(const char axis, const int nb=0) const {
37125       CImgList<T> res;
37126       cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
37127       return res;
37128     }
37129 
37130     //! Insert image \p img at the end of the list (STL-compliant name).
37131     template<typename t>
37132     CImgList<T>& push_back(const CImg<t>& img) {
37133       return insert(img);
37134     }
37135 
37136     //! Insert image \p img at the front of the list (STL-compliant name).
37137     template<typename t>
37138     CImgList<T>& push_front(const CImg<t>& img) {
37139       return insert(img,0);
37140     }
37141 
37142     //! Insert list \p list at the end of the current list (STL-compliant name).
37143     template<typename t>
37144     CImgList<T>& push_back(const CImgList<t>& list) {
37145       return insert(list);
37146     }
37147 
37148     //! Insert list \p list at the front of the current list (STL-compliant name).
37149     template<typename t>
37150     CImgList<T>& push_front(const CImgList<t>& list) {
37151       return insert(list,0);
37152     }
37153 
37154     //! Remove last element of the list (STL-compliant name).
37155     CImgList<T>& pop_back() {
37156       return remove(_width-1);
37157     }
37158 
37159     //! Remove first element of the list (STL-compliant name).
37160     CImgList<T>& pop_front() {
37161       return remove(0);
37162     }
37163 
37164     //! Remove the element pointed by iterator \p iter (STL-compliant name).
37165     CImgList<T>& erase(const iterator iter) {
37166       return remove(iter-_data);
37167     }
37168 
37169     //@}
37170     //----------------------------------
37171     //
37172     //! \name Data Input
37173     //@{
37174     //----------------------------------
37175 
37176     //! Load an image list from a file.
37177     CImgList<T>& load(const char *const filename) {
37178       if (!filename)
37179         throw CImgArgumentException(_cimglist_instance
37180                                     "load() : Specified filename is (null).",
37181                                     cimglist_instance);
37182 
37183       const char *const ext = cimg::split_filename(filename);
37184       const unsigned int omode = cimg::exception_mode();
37185       cimg::exception_mode() = 0;
37186       try {
37187 #ifdef cimglist_load_plugin
37188         cimglist_load_plugin(filename);
37189 #endif
37190 #ifdef cimglist_load_plugin1
37191         cimglist_load_plugin1(filename);
37192 #endif
37193 #ifdef cimglist_load_plugin2
37194         cimglist_load_plugin2(filename);
37195 #endif
37196 #ifdef cimglist_load_plugin3
37197         cimglist_load_plugin3(filename);
37198 #endif
37199 #ifdef cimglist_load_plugin4
37200         cimglist_load_plugin4(filename);
37201 #endif
37202 #ifdef cimglist_load_plugin5
37203         cimglist_load_plugin5(filename);
37204 #endif
37205 #ifdef cimglist_load_plugin6
37206         cimglist_load_plugin6(filename);
37207 #endif
37208 #ifdef cimglist_load_plugin7
37209         cimglist_load_plugin7(filename);
37210 #endif
37211 #ifdef cimglist_load_plugin8
37212         cimglist_load_plugin8(filename);
37213 #endif
37214         if (!cimg::strcasecmp(ext,"tif") ||
37215             !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
37216         else if (!cimg::strcasecmp(ext,"cimg") ||
37217                  !cimg::strcasecmp(ext,"cimgz") ||
37218                  !ext[0]) load_cimg(filename);
37219         else if (!cimg::strcasecmp(ext,"rec") ||
37220                  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
37221         else if (!cimg::strcasecmp(ext,"avi") ||
37222                  !cimg::strcasecmp(ext,"mov") ||
37223                  !cimg::strcasecmp(ext,"asf") ||
37224                  !cimg::strcasecmp(ext,"divx") ||
37225                  !cimg::strcasecmp(ext,"flv") ||
37226                  !cimg::strcasecmp(ext,"mpg") ||
37227                  !cimg::strcasecmp(ext,"m1v") ||
37228                  !cimg::strcasecmp(ext,"m2v") ||
37229                  !cimg::strcasecmp(ext,"m4v") ||
37230                  !cimg::strcasecmp(ext,"mjp") ||
37231                  !cimg::strcasecmp(ext,"mkv") ||
37232                  !cimg::strcasecmp(ext,"mpe") ||
37233                  !cimg::strcasecmp(ext,"movie") ||
37234                  !cimg::strcasecmp(ext,"ogm") ||
37235                  !cimg::strcasecmp(ext,"qt") ||
37236                  !cimg::strcasecmp(ext,"rm") ||
37237                  !cimg::strcasecmp(ext,"vob") ||
37238                  !cimg::strcasecmp(ext,"wmv") ||
37239                  !cimg::strcasecmp(ext,"xvid") ||
37240                  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
37241         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
37242         else throw CImgIOException("CImgList<%s>::load()",
37243                                    pixel_type());
37244       } catch (CImgIOException& e) {
37245         if (!cimg::strncasecmp(e.what(),"cimg::fopen()",13)) {
37246           cimg::exception_mode() = omode;
37247           throw CImgIOException(_cimglist_instance
37248                                 "load() : Failed to open file '%s'.",
37249                                 cimglist_instance,
37250                                 filename);
37251         } else try {
37252           assign(1);
37253           _data->load(filename);
37254         } catch (CImgException&) {
37255           throw CImgIOException(_cimglist_instance
37256                                 "load() : Failed to recognize format of file '%s'.",
37257                                 cimglist_instance,
37258                                 filename);
37259         }
37260       }
37261       cimg::exception_mode() = omode;
37262       return *this;
37263     }
37264 
37265     static CImgList<T> get_load(const char *const filename) {
37266       return CImgList<T>().load(filename);
37267     }
37268 
37269     //! Load an image list from a .cimg file.
37270     CImgList<T>& load_cimg(const char *const filename) {
37271       return _load_cimg(0,filename);
37272     }
37273 
37274     static CImgList<T> get_load_cimg(const char *const filename) {
37275       return CImgList<T>().load_cimg(filename);
37276     }
37277 
37278     //! Load an image list from a .cimg file.
37279     CImgList<T>& load_cimg(std::FILE *const file) {
37280       return _load_cimg(file,0);
37281     }
37282 
37283     static CImgList<T> get_load_cimg(std::FILE *const file) {
37284       return CImgList<T>().load_cimg(file);
37285     }
37286 
37287     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
37288 #ifdef cimg_use_zlib
37289 #define _cimgz_load_cimg_case(Tss) { \
37290    Bytef *const cbuf = new Bytef[csiz]; \
37291    cimg::fread(cbuf,csiz,nfile); \
37292    raw.assign(W,H,D,C); \
37293    unsigned long destlen = (unsigned long)raw.size()*sizeof(T); \
37294    uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
37295    delete[] cbuf; \
37296    const Tss *ptrs = raw._data; \
37297    for (unsigned int off = raw.size(); off; --off) *(ptrd++) = (T)*(ptrs++); \
37298 }
37299 #else
37300 #define _cimgz_load_cimg_case(Tss) \
37301    throw CImgIOException(_cimglist_instance \
37302                          "load_cimg() : Unable to load compressed data from file '%s' unless zlib is enabled.", \
37303                          cimglist_instance, \
37304                          filename?filename:"(FILE*)");
37305 #endif
37306 
37307 #define _cimg_load_cimg_case(Ts,Tss) \
37308       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
37309         for (unsigned int l = 0; l<N; ++l) { \
37310           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
37311           W = H = D = C = 0; csiz = 0; \
37312           if ((err = std::sscanf(tmp,"%u %u %u %u #%u",&W,&H,&D,&C,&csiz))<4) \
37313             throw CImgIOException(_cimglist_instance \
37314                                   "load_cimg() : Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
37315                                   cimglist_instance, \
37316                                   W,H,D,C,l,filename?filename:("(FILE*)")); \
37317           if (W*H*D*C>0) { \
37318             CImg<Tss> raw; \
37319             CImg<T> &img = _data[l]; \
37320             img.assign(W,H,D,C); \
37321             T *ptrd = img._data; \
37322             if (err==5) _cimgz_load_cimg_case(Tss) \
37323             else for (int toread = (int)img.size(); toread>0; ) { \
37324               raw.assign(cimg::min(toread,cimg_iobuffer)); \
37325               cimg::fread(raw._data,raw._width,nfile); \
37326               if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
37327               toread-=raw._width; \
37328               const Tss *ptrs = raw._data; \
37329               for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
37330             } \
37331           } \
37332         } \
37333         loaded = true; \
37334       }
37335 
37336       if (!filename && !file)
37337         throw CImgArgumentException(_cimglist_instance
37338                                     "load_cimg() : Specified filename is (null).",
37339                                     cimglist_instance);
37340 
37341       const int cimg_iobuffer = 12*1024*1024;
37342       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
37343       bool loaded = false, endian = cimg::endianness();
37344       char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
37345       unsigned int j, err, N = 0, W, H, D, C, csiz;
37346       int i;
37347       do {
37348         j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
37349       } while (*tmp=='#' && i!=EOF);
37350       err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
37351       if (err<2) {
37352         if (!file) cimg::fclose(nfile);
37353         throw CImgIOException(_cimglist_instance
37354                               "load_cimg() : CImg header not found in file '%s'.",
37355                               cimglist_instance,
37356                               filename?filename:"(FILE*)");
37357       }
37358       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
37359       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
37360       assign(N);
37361       _cimg_load_cimg_case("bool",bool);
37362       _cimg_load_cimg_case("unsigned_char",unsigned char);
37363       _cimg_load_cimg_case("uchar",unsigned char);
37364       _cimg_load_cimg_case("char",char);
37365       _cimg_load_cimg_case("unsigned_short",unsigned short);
37366       _cimg_load_cimg_case("ushort",unsigned short);
37367       _cimg_load_cimg_case("short",short);
37368       _cimg_load_cimg_case("unsigned_int",unsigned int);
37369       _cimg_load_cimg_case("uint",unsigned int);
37370       _cimg_load_cimg_case("int",int);
37371       _cimg_load_cimg_case("unsigned_long",unsigned long);
37372       _cimg_load_cimg_case("ulong",unsigned long);
37373       _cimg_load_cimg_case("long",long);
37374       _cimg_load_cimg_case("float",float);
37375       _cimg_load_cimg_case("double",double);
37376       if (!loaded) {
37377         if (!file) cimg::fclose(nfile);
37378         throw CImgIOException(_cimglist_instance
37379                               "load_cimg() : Unsupported pixel type '%s' for file '%s'.",
37380                               cimglist_instance,
37381                               str_pixeltype,filename?filename:"(FILE*)");
37382       }
37383       if (!file) cimg::fclose(nfile);
37384       return *this;
37385     }
37386 
37387     //! Load a sub-image list from a non compressed .cimg file.
37388     CImgList<T>& load_cimg(const char *const filename,
37389                            const unsigned int n0, const unsigned int n1,
37390                            const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
37391                            const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
37392       return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
37393     }
37394 
37395     static CImgList<T> get_load_cimg(const char *const filename,
37396                                      const unsigned int n0, const unsigned int n1,
37397                                      const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
37398                                      const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
37399       return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
37400     }
37401 
37402     //! Load a sub-image list from a non compressed .cimg file.
37403     CImgList<T>& load_cimg(std::FILE *const file,
37404                            const unsigned int n0, const unsigned int n1,
37405                            const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
37406                            const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
37407       return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
37408     }
37409 
37410     static CImgList<T> get_load_cimg(std::FILE *const file,
37411                                      const unsigned int n0, const unsigned int n1,
37412                                      const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
37413                                      const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
37414       return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
37415     }
37416 
37417     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
37418                             const unsigned int n0, const unsigned int n1,
37419                             const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
37420                             const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
37421 #define _cimg_load_cimg_case2(Ts,Tss) \
37422       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
37423         for (unsigned int l = 0; l<=nn1; ++l) { \
37424           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
37425           W = H = D = C = 0; \
37426           if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
37427             throw CImgIOException(_cimglist_instance \
37428                                   "load_cimg() : Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
37429                                   cimglist_instance, \
37430                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
37431           if (W*H*D*C>0) { \
37432             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
37433             else { \
37434               const unsigned int \
37435                 nx1 = x1>=W?W-1:x1, \
37436                 ny1 = y1>=H?H-1:y1, \
37437                 nz1 = z1>=D?D-1:z1, \
37438                 nc1 = c1>=C?C-1:c1; \
37439               CImg<Tss> raw(1 + nx1 - x0); \
37440               CImg<T> &img = _data[l - n0]; \
37441               img.assign(1 + nx1 - x0,1 + ny1 - y0,1 + nz1 - z0,1 + nc1 - c0); \
37442               T *ptrd = img._data; \
37443               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
37444               if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
37445               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
37446                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
37447                 if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
37448                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
37449                   const unsigned int skipyb = y0*W*sizeof(Tss); \
37450                   if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
37451                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
37452                     const unsigned int skipxb = x0*sizeof(Tss); \
37453                     if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
37454                     cimg::fread(raw._data,raw._width,nfile); \
37455                     if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
37456                     const Tss *ptrs = raw._data; \
37457                     for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
37458                     const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \
37459                     if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
37460                   } \
37461                   const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \
37462                   if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
37463                 } \
37464                 const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \
37465                 if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
37466               } \
37467               const unsigned int skipve = (C-1-nc1)*W*H*D*sizeof(Tss); \
37468               if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
37469             } \
37470           } \
37471         } \
37472         loaded = true; \
37473       }
37474 
37475       if (!filename && !file)
37476         throw CImgArgumentException(_cimglist_instance
37477                                     "load_cimg() : Specified filename is (null).",
37478                                     cimglist_instance);
37479 
37480       if (n1<n0 || x1<x0 || y1<y0 || z1<z0 || c1<c0)
37481         throw CImgArgumentException(_cimglist_instance
37482                                     "load_cimg() : Invalid specified sub-region coordinates [%u->%u] (%u,%u,%u,%u)->(%u,%u,%u,%u) for file '%s'.",
37483                                     cimglist_instance,
37484                                     n0,n1,x0,y0,z0,c0,x1,y1,z1,filename?filename:"(FILE*)");
37485 
37486       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
37487       bool loaded = false, endian = cimg::endianness();
37488       char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
37489       unsigned int j, err, N, W, H, D, C;
37490       int i;
37491       j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
37492       err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
37493       if (err<2) {
37494         if (!file) cimg::fclose(nfile);
37495         throw CImgIOException(_cimglist_instance
37496                               "load_cimg() : CImg header not found in file '%s'.",
37497                               cimglist_instance,
37498                               filename?filename:"(FILE*)");
37499       }
37500       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
37501       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
37502       const unsigned int nn1 = n1>=N?N-1:n1;
37503       assign(1+nn1-n0);
37504       _cimg_load_cimg_case2("bool",bool);
37505       _cimg_load_cimg_case2("unsigned_char",unsigned char);
37506       _cimg_load_cimg_case2("uchar",unsigned char);
37507       _cimg_load_cimg_case2("char",char);
37508       _cimg_load_cimg_case2("unsigned_short",unsigned short);
37509       _cimg_load_cimg_case2("ushort",unsigned short);
37510       _cimg_load_cimg_case2("short",short);
37511       _cimg_load_cimg_case2("unsigned_int",unsigned int);
37512       _cimg_load_cimg_case2("uint",unsigned int);
37513       _cimg_load_cimg_case2("int",int);
37514       _cimg_load_cimg_case2("unsigned_long",unsigned long);
37515       _cimg_load_cimg_case2("ulong",unsigned long);
37516       _cimg_load_cimg_case2("long",long);
37517       _cimg_load_cimg_case2("float",float);
37518       _cimg_load_cimg_case2("double",double);
37519       if (!loaded) {
37520         if (!file) cimg::fclose(nfile);
37521         throw CImgIOException(_cimglist_instance
37522                               "load_cimg() : Unsupported pixel type '%s' for file '%s'.",
37523                               cimglist_instance,
37524                               str_pixeltype,filename?filename:"(FILE*)");
37525       }
37526       if (!file) cimg::fclose(nfile);
37527       return *this;
37528     }
37529 
37530     //! Load an image list from a PAR/REC (Philips) file.
37531     CImgList<T>& load_parrec(const char *const filename) {
37532       if (!filename)
37533         throw CImgArgumentException(_cimglist_instance
37534                                     "load_parrec() : Specified filename is (null).",
37535                                     cimglist_instance);
37536 
37537       char body[1024] = { 0 }, filenamepar[1024] = { 0 }, filenamerec[1024] = { 0 };
37538       const char *const ext = cimg::split_filename(filename,body);
37539       if (!std::strcmp(ext,"par")) { std::strcpy(filenamepar,filename); std::sprintf(filenamerec,"%s.rec",body); }
37540       if (!std::strcmp(ext,"PAR")) { std::strcpy(filenamepar,filename); std::sprintf(filenamerec,"%s.REC",body); }
37541       if (!std::strcmp(ext,"rec")) { std::strcpy(filenamerec,filename); std::sprintf(filenamepar,"%s.par",body); }
37542       if (!std::strcmp(ext,"REC")) { std::strcpy(filenamerec,filename); std::sprintf(filenamepar,"%s.PAR",body); }
37543       std::FILE *file = cimg::fopen(filenamepar,"r");
37544 
37545       // Parse header file
37546       CImgList<floatT> st_slices;
37547       CImgList<uintT> st_global;
37548       int err;
37549       char line[256] = { 0 };
37550       do { err=std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (line[0]=='#' || line[0]=='.'));
37551       do {
37552         unsigned int sn,sizex,sizey,pixsize;
37553         float rs,ri,ss;
37554         err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&sizex,&sizey,&ri,&rs,&ss);
37555         if (err==7) {
37556           CImg<floatT>::vector((float)sn,(float)pixsize,(float)sizex,(float)sizey,ri,rs,ss,0).move_to(st_slices);
37557           unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
37558           if (i==st_global._width) CImg<uintT>::vector(sizex,sizey,sn).move_to(st_global);
37559           else {
37560             CImg<uintT> &vec = st_global[i];
37561             if (sizex>vec[0]) vec[0] = sizex;
37562             if (sizey>vec[1]) vec[1] = sizey;
37563             vec[2] = sn;
37564           }
37565           st_slices[st_slices._width-1][7] = (float)i;
37566         }
37567       } while (err==7);
37568 
37569       // Read data
37570       std::FILE *file2 = cimg::fopen(filenamerec,"rb");
37571       cimglist_for(st_global,l) {
37572         const CImg<uintT>& vec = st_global[l];
37573         CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
37574       }
37575 
37576       cimglist_for(st_slices,l) {
37577         const CImg<floatT>& vec = st_slices[l];
37578         const unsigned int
37579           sn = (unsigned int)vec[0]-1,
37580           pixsize = (unsigned int)vec[1],
37581           sizex = (unsigned int)vec[2],
37582           sizey = (unsigned int)vec[3],
37583           imn = (unsigned int)vec[7];
37584         const float ri = vec[4], rs = vec[5], ss = vec[6];
37585         switch (pixsize) {
37586         case 8 : {
37587           CImg<ucharT> buf(sizex,sizey);
37588           cimg::fread(buf._data,sizex*sizey,file2);
37589           if (cimg::endianness()) cimg::invert_endianness(buf._data,sizex*sizey);
37590           CImg<T>& img = (*this)[imn];
37591           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
37592         } break;
37593         case 16 : {
37594           CImg<ushortT> buf(sizex,sizey);
37595           cimg::fread(buf._data,sizex*sizey,file2);
37596           if (cimg::endianness()) cimg::invert_endianness(buf._data,sizex*sizey);
37597           CImg<T>& img = (*this)[imn];
37598           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
37599         } break;
37600         case 32 : {
37601           CImg<uintT> buf(sizex,sizey);
37602           cimg::fread(buf._data,sizex*sizey,file2);
37603           if (cimg::endianness()) cimg::invert_endianness(buf._data,sizex*sizey);
37604           CImg<T>& img = (*this)[imn];
37605           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
37606         } break;
37607         default :
37608           cimg::fclose(file);
37609           cimg::fclose(file2);
37610           throw CImgIOException(_cimglist_instance
37611                                 "load_parrec() : Unsupported %d-bits pixel type for file '%s'.",
37612                                 cimglist_instance,
37613                                 pixsize,filename);
37614         }
37615       }
37616       cimg::fclose(file);
37617       cimg::fclose(file2);
37618       if (!_width)
37619         throw CImgIOException(_cimglist_instance
37620                               "load_parrec() : Failed to recognize valid PAR-REC data in file '%s'.",
37621                               cimglist_instance,
37622                               filename);
37623       return *this;
37624     }
37625 
37626     static CImgList<T> get_load_parrec(const char *const filename) {
37627       return CImgList<T>().load_parrec(filename);
37628     }
37629 
37630     //! Load an image sequence from a YUV file.
37631     CImgList<T>& load_yuv(const char *const filename,
37632                           const unsigned int sizex, const unsigned int sizey,
37633                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
37634                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
37635       return _load_yuv(0,filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
37636     }
37637 
37638     static CImgList<T> get_load_yuv(const char *const filename,
37639                                     const unsigned int sizex, const unsigned int sizey=1,
37640                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
37641                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
37642       return CImgList<T>().load_yuv(filename,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
37643     }
37644 
37645     //! Load an image sequence from a YUV file.
37646     CImgList<T>& load_yuv(std::FILE *const file,
37647                           const unsigned int sizex, const unsigned int sizey,
37648                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
37649                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
37650       return _load_yuv(file,0,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
37651     }
37652 
37653     static CImgList<T> get_load_yuv(std::FILE *const file,
37654                                     const unsigned int sizex, const unsigned int sizey=1,
37655                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
37656                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
37657       return CImgList<T>().load_yuv(file,sizex,sizey,first_frame,last_frame,step_frame,yuv2rgb);
37658     }
37659 
37660     CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
37661                            const unsigned int sizex, const unsigned int sizey,
37662                            const unsigned int first_frame, const unsigned int last_frame,
37663                            const unsigned int step_frame, const bool yuv2rgb) {
37664       if (!filename && !file)
37665         throw CImgArgumentException(_cimglist_instance
37666                                     "load_yuv() : Specified filename is (null).",
37667                                     cimglist_instance);
37668       if (sizex%2 || sizey%2)
37669         throw CImgArgumentException(_cimglist_instance
37670                                     "load_yuv() : Invalid odd XY dimensions %ux%u in file '%s'.",
37671                                     cimglist_instance,
37672                                     sizex,sizey,filename?filename:"(FILE*)");
37673       if (!sizex || !sizey)
37674         throw CImgArgumentException(_cimglist_instance
37675                                     "load_yuv() : Invalid sequence size (%u,%u) in file '%s'.",
37676                                     cimglist_instance,
37677                                     sizex,sizey,filename?filename:"(FILE*)");
37678 
37679       const unsigned int
37680         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
37681         nlast_frame = first_frame<last_frame?last_frame:first_frame,
37682         nstep_frame = step_frame?step_frame:1;
37683 
37684       CImg<ucharT> tmp(sizex,sizey,1,3), UV(sizex/2,sizey/2,1,2);
37685       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
37686       bool stopflag = false;
37687       int err;
37688       if (nfirst_frame) {
37689         err = std::fseek(nfile,nfirst_frame*(sizex*sizey + sizex*sizey/2),SEEK_CUR);
37690         if (err) {
37691           if (!file) cimg::fclose(nfile);
37692           throw CImgIOException(_cimglist_instance
37693                                 "load_yuv() : File '%s' doesn't contain frame number %u.",
37694                                 cimglist_instance,
37695                                 filename?filename:"(FILE*)",nfirst_frame);
37696         }
37697       }
37698       unsigned int frame;
37699       for (frame = nfirst_frame; !stopflag && frame<=nlast_frame; frame+=nstep_frame) {
37700         tmp.fill(0);
37701         // *TRY* to read the luminance part, do not replace by cimg::fread !
37702         err = (int)std::fread((void*)(tmp._data),1,(size_t)(tmp._width*tmp._height),nfile);
37703         if (err!=(int)(tmp._width*tmp._height)) {
37704           stopflag = true;
37705           if (err>0)
37706             cimg::warn(_cimglist_instance
37707                        "load_yuv() : File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.",
37708                        cimglist_instance,
37709                        filename?filename:"(FILE*)",sizex,sizey);
37710         } else {
37711           UV.fill(0);
37712           // *TRY* to read the luminance part, do not replace by cimg::fread !
37713           err = (int)std::fread((void*)(UV._data),1,(size_t)(UV.size()),nfile);
37714           if (err!=(int)(UV.size())) {
37715             stopflag = true;
37716             if (err>0)
37717               cimg::warn(_cimglist_instance
37718                          "load_yuv() : File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.",
37719                          cimglist_instance,
37720                          filename?filename:"(FILE*)",sizex,sizey);
37721           } else {
37722             cimg_forXY(UV,x,y) {
37723               const int x2 = x*2, y2 = y*2;
37724               tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0);
37725               tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1);
37726             }
37727             if (yuv2rgb) tmp.YCbCrtoRGB();
37728             insert(tmp);
37729             if (nstep_frame>1) std::fseek(nfile,(nstep_frame-1)*(sizex*sizey + sizex*sizey/2),SEEK_CUR);
37730           }
37731         }
37732       }
37733       if (stopflag && nlast_frame!=~0U && frame!=nlast_frame)
37734         cimg::warn(_cimglist_instance
37735                    "load_yuv() : Frame %d not reached since only %u frames were found in file '%s'.",
37736                    cimglist_instance,
37737                    nlast_frame,frame-1,filename?filename:"(FILE*)");
37738 
37739       if (!file) cimg::fclose(nfile);
37740       return *this;
37741     }
37742 
37743     //! Load an image from a video file, using ffmpeg libraries.
37744     // This piece of code has been firstly created by David Starweather (starkdg(at)users(dot)sourceforge(dot)net)
37745     // I modified it afterwards for direct inclusion in the library core.
37746     CImgList<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
37747                              const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false) {
37748       if (!filename)
37749         throw CImgArgumentException(_cimglist_instance
37750                                     "load_ffmpeg() : Specified filename is (null).",
37751                                     cimglist_instance);
37752 
37753       const unsigned int
37754         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
37755         nlast_frame = first_frame<last_frame?last_frame:first_frame,
37756         nstep_frame = step_frame?step_frame:1;
37757       assign();
37758 
37759 #ifndef cimg_use_ffmpeg
37760       if ((nfirst_frame || nlast_frame!=~0U || nstep_frame>1) || (resume && (pixel_format || !pixel_format)))
37761         throw CImgArgumentException(_cimglist_instance
37762                                     "load_ffmpeg() : Unable to load sub-frames from file '%s' unless libffmpeg is enabled.",
37763                                     cimglist_instance,
37764                                     filename);
37765 
37766       return load_ffmpeg_external(filename);
37767 #else
37768       const PixelFormat ffmpeg_pixfmt = pixel_format?PIX_FMT_RGB24:PIX_FMT_GRAY8;
37769       avcodec_register_all();
37770       av_register_all();
37771       static AVFormatContext *format_ctx = 0;
37772       static AVCodecContext *codec_ctx = 0;
37773       static AVCodec *codec = 0;
37774       static AVFrame *avframe = avcodec_alloc_frame(), *converted_frame = avcodec_alloc_frame();
37775       static int vstream = 0;
37776 
37777       if (resume) {
37778         if (!format_ctx || !codec_ctx || !codec || !avframe || !converted_frame)
37779           throw CImgArgumentException(_cimglist_instance
37780                                       "load_ffmpeg() : Failed to resume loading of file '%s', due to unallocated FFMPEG structures.",
37781                                       cimglist_instance,
37782                                       filename);
37783       } else {
37784         // Open video file, find main video stream and codec.
37785         if (format_ctx) av_close_input_file(format_ctx);
37786         if (av_open_input_file(&format_ctx,filename,0,0,0)!=0)
37787           throw CImgIOException(_cimglist_instance
37788                                 "load_ffmpeg() : Failed to open file '%s'.",
37789                                 cimglist_instance,
37790                                 filename);
37791 
37792         if (!avframe || !converted_frame || av_find_stream_info(format_ctx)<0) {
37793           av_close_input_file(format_ctx); format_ctx = 0;
37794           return load_ffmpeg_external(filename);
37795         }
37796 #if cimg_verbosity>=3
37797         dump_format(format_ctx,0,0,0);
37798 #endif
37799 
37800         // Special command : Return informations on main video stream.
37801         // as a vector 1x4 containing : (nb_frames,width,height,fps).
37802         if (!first_frame && !last_frame && !step_frame) {
37803           for (vstream = 0; vstream<(int)(format_ctx->nb_streams); ++vstream)
37804             if (format_ctx->streams[vstream]->codec->codec_type==CODEC_TYPE_VIDEO) break;
37805           if (vstream==(int)format_ctx->nb_streams) assign();
37806           else {
37807             CImgList<doubleT> timestamps;
37808             int nb_frames;
37809             AVPacket packet;
37810             // Count frames and store timestamps.
37811             for (nb_frames = 0; av_read_frame(format_ctx,&packet)>=0; av_free_packet(&packet))
37812               if (packet.stream_index==vstream) {
37813                 CImg<doubleT>::vector((double)packet.pts).move_to(timestamps);
37814                 ++nb_frames;
37815               }
37816             // Get frame with, height and fps.
37817             const int
37818               framew = format_ctx->streams[vstream]->codec->width,
37819               frameh = format_ctx->streams[vstream]->codec->height;
37820             const float
37821               num = (float)(format_ctx->streams[vstream]->r_frame_rate).num,
37822               den = (float)(format_ctx->streams[vstream]->r_frame_rate).den,
37823               fps = num/den;
37824             // Return infos as a list.
37825             assign(2);
37826             (*this)[0].assign(1,4).fill((T)nb_frames,(T)framew,(T)frameh,(T)fps);
37827             (*this)[1] = (timestamps>'y');
37828           }
37829           av_close_input_file(format_ctx); format_ctx = 0;
37830           return *this;
37831         }
37832 
37833         for (vstream = 0; vstream<(int)(format_ctx->nb_streams) &&
37834                format_ctx->streams[vstream]->codec->codec_type!=CODEC_TYPE_VIDEO; ) ++vstream;
37835         if (vstream==(int)format_ctx->nb_streams) {
37836           av_close_input_file(format_ctx); format_ctx = 0;
37837           return load_ffmpeg_external(filename);
37838         }
37839         codec_ctx = format_ctx->streams[vstream]->codec;
37840         codec = avcodec_find_decoder(codec_ctx->codec_id);
37841         if (!codec) {
37842           return load_ffmpeg_external(filename);
37843         }
37844         if (avcodec_open(codec_ctx,codec)<0) { // Open codec
37845           return load_ffmpeg_external(filename);
37846         }
37847       }
37848 
37849       // Read video frames
37850       const unsigned int numBytes = avpicture_get_size(ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
37851       uint8_t *const buffer = new uint8_t[numBytes];
37852       avpicture_fill((AVPicture *)converted_frame,buffer,ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
37853       const T foo = (T)0;
37854       AVPacket packet;
37855       for (unsigned int frame = 0, next_frame = nfirst_frame; frame<=nlast_frame && av_read_frame(format_ctx,&packet)>=0; ) {
37856         if (packet.stream_index==(int)vstream) {
37857           int decoded = 0;
37858 #if LIBAVCODEC_VERSION_MAJOR<53
37859           avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data, packet.size);
37860 #else
37861           avcodec_decode_video2(codec_ctx,avframe,&decoded,&packet);
37862 #endif
37863           if (decoded) {
37864             if (frame==next_frame) {
37865               SwsContext *c = sws_getContext(codec_ctx->width,codec_ctx->height,codec_ctx->pix_fmt,codec_ctx->width,
37866                                              codec_ctx->height,ffmpeg_pixfmt,1,0,0,0);
37867               sws_scale(c,avframe->data,avframe->linesize,0,codec_ctx->height,converted_frame->data,converted_frame->linesize);
37868               if (ffmpeg_pixfmt==PIX_FMT_RGB24) {
37869                 CImg<ucharT> next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true);
37870                 next_image._get_permute_axes("yzcx",foo).move_to(*this);
37871               } else {
37872                 CImg<ucharT> next_image(*converted_frame->data,1,codec_ctx->width,codec_ctx->height,1,true);
37873                 next_image._get_permute_axes("yzcx",foo).move_to(*this);
37874               }
37875               next_frame+=nstep_frame;
37876             }
37877             ++frame;
37878           }
37879           av_free_packet(&packet);
37880           if (next_frame>nlast_frame) break;
37881         }
37882       }
37883       delete[] buffer;
37884 #endif
37885       return *this;
37886     }
37887 
37888     static CImgList<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
37889                                        const unsigned int step_frame=1, const bool pixel_format=true) {
37890       return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format);
37891     }
37892 
37893     //! Load an image from a video file (MPEG,AVI) using the external tool 'ffmpeg'.
37894     CImgList<T>& load_ffmpeg_external(const char *const filename) {
37895       if (!filename)
37896         throw CImgArgumentException(_cimglist_instance
37897                                     "load_ffmpeg_external() : Specified filename is (null).",
37898                                     cimglist_instance);
37899 
37900       char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
37901       std::FILE *file = 0;
37902       do {
37903         std::sprintf(filetmp,"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37904         std::sprintf(filetmp2,"%s_000001.ppm",filetmp);
37905         if ((file=std::fopen(filetmp2,"rb"))!=0) std::fclose(file);
37906       } while (file);
37907       std::sprintf(filetmp2,"%s_%%6d.ppm",filetmp);
37908 #if cimg_OS!=2
37909       std::sprintf(command,"%s -i \"%s\" %s >/dev/null 2>&1",cimg::ffmpeg_path(),filename,filetmp2);
37910 #else
37911       std::sprintf(command,"\"%s -i \"%s\" %s\" >NUL 2>&1",cimg::ffmpeg_path(),filename,filetmp2);
37912 #endif
37913       cimg::system(command,0);
37914       const unsigned int omode = cimg::exception_mode();
37915       cimg::exception_mode() = 0;
37916       assign();
37917       unsigned int i = 1;
37918       for (bool stopflag = false; !stopflag; ++i) {
37919         std::sprintf(filetmp2,"%s_%.6u.ppm",filetmp,i);
37920         CImg<T> img;
37921         try { img.load_pnm(filetmp2); }
37922         catch (CImgException&) { stopflag = true; }
37923         if (img) { img.move_to(*this); std::remove(filetmp2); }
37924       }
37925       cimg::exception_mode() = omode;
37926       if (is_empty())
37927         throw CImgIOException(_cimglist_instance
37928                               "load_ffmpeg_external() : Failed to open file '%s' with external command 'ffmpeg'.",
37929                               cimglist_instance,
37930                               filename);
37931       return *this;
37932     }
37933 
37934     static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
37935       return CImgList<T>().load_ffmpeg_external(filename);
37936     }
37937 
37938     //! Load a gzipped list, using external tool 'gunzip'.
37939     CImgList<T>& load_gzip_external(const char *const filename) {
37940       if (!filename)
37941         throw CImgIOException(_cimglist_instance
37942                               "load_gzip_external() : Specified filename is (null).",
37943                               cimglist_instance);
37944 
37945       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
37946       const char
37947         *ext = cimg::split_filename(filename,body),
37948         *ext2 = cimg::split_filename(body,0);
37949       std::FILE *file = 0;
37950       do {
37951         if (!cimg::strcasecmp(ext,"gz")) {
37952           if (*ext2) std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
37953           else std::sprintf(filetmp,"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37954         } else {
37955           if (*ext) std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
37956           else std::sprintf(filetmp,"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37957         }
37958         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
37959       } while (file);
37960       std::sprintf(command,"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp);
37961       cimg::system(command);
37962       if (!(file = std::fopen(filetmp,"rb"))) {
37963         cimg::fclose(cimg::fopen(filename,"r"));
37964         throw CImgIOException(_cimglist_instance
37965                               "load_gzip_external() : Failed to open file '%s'.",
37966                               cimglist_instance,
37967                               filename);
37968 
37969       } else cimg::fclose(file);
37970       load(filetmp);
37971       std::remove(filetmp);
37972       return *this;
37973     }
37974 
37975     static CImgList<T> get_load_gzip_external(const char *const filename) {
37976       return CImgList<T>().load_gzip_external(filename);
37977     }
37978 
37979     //! Load a 3d object from a .OFF file.
37980     template<typename tf, typename tc>
37981     CImgList<T>& load_off(const char *const filename,
37982                           CImgList<tf>& primitives, CImgList<tc>& colors) {
37983       return get_load_off(filename,primitives,colors).move_to(*this);
37984     }
37985 
37986     template<typename tf, typename tc>
37987       static CImgList<T> get_load_off(const char *const filename,
37988                                       CImgList<tf>& primitives, CImgList<tc>& colors) {
37989       return CImg<T>().load_off(filename,primitives,colors)<'x';
37990     }
37991 
37992     //! Load a TIFF file.
37993     CImgList<T>& load_tiff(const char *const filename,
37994                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
37995                            const unsigned int step_frame=1) {
37996       const unsigned int
37997         nfirst_frame = first_frame<last_frame?first_frame:last_frame,
37998         nstep_frame = step_frame?step_frame:1;
37999       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
38000 #ifndef cimg_use_tiff
38001       if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
38002         throw CImgArgumentException(_cimglist_instance
38003                                     "load_tiff() : Unable to load sub-images from file '%s' unless libtiff is enabled.",
38004                                     cimglist_instance,
38005                                     filename);
38006 
38007       return assign(CImg<T>::get_load_tiff(filename));
38008 #else
38009       TIFF *tif = TIFFOpen(filename,"r");
38010       if (tif) {
38011         unsigned int nb_images = 0;
38012         do ++nb_images; while (TIFFReadDirectory(tif));
38013         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
38014           cimg::warn(_cimglist_instance
38015                      "load_tiff() : Invalid specified frame range is [%u,%u] (step %u) since file '%s' contains %u image(s).",
38016                      cimglist_instance,
38017                      nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
38018 
38019         if (nfirst_frame>=nb_images) return assign();
38020         if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
38021         assign(1+(nlast_frame-nfirst_frame)/nstep_frame);
38022         TIFFSetDirectory(tif,0);
38023 #if cimg_verbosity>=3
38024         TIFFSetWarningHandler(0);
38025         TIFFSetErrorHandler(0);
38026 #endif
38027         cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame);
38028         TIFFClose(tif);
38029       } else throw CImgException(_cimglist_instance
38030                                  "load_tiff() : Failed to open file '%s'.",
38031                                  cimglist_instance,
38032                                  filename);
38033       return *this;
38034 #endif
38035     }
38036 
38037     static CImgList<T> get_load_tiff(const char *const filename,
38038                                      const unsigned int first_frame=0, const unsigned int last_frame=~0U,
38039                                      const unsigned int step_frame=1) {
38040       return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame);
38041     }
38042 
38043     //@}
38044     //----------------------------------
38045     //
38046     //! \name Data Output
38047     //@{
38048     //----------------------------------
38049 
38050     //! Print informations about the list on the standard output.
38051     const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
38052       unsigned int msiz = 0;
38053       cimglist_for(*this,l) msiz+=_data[l].size();
38054       msiz*=sizeof(T);
38055       const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2);
38056       char ntitle[64] = { 0 };
38057       if (!title) std::sprintf(ntitle,"CImgList<%s>",pixel_type());
38058       std::fprintf(cimg::output(),"%s: this = %p, size = %u [%u %s], data = (CImg<%s>*)%p",
38059                    title?title:ntitle,(void*)this,_width,
38060                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
38061                    mdisp==0?"b":(mdisp==1?"Kb":"Mb"),
38062                    pixel_type(),(void*)begin());
38063       if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end()-1));
38064       else std::fprintf(cimg::output(),".\n");
38065 
38066       char tmp[16] = { 0 };
38067       cimglist_for(*this,ll) {
38068         std::sprintf(tmp,"[%d]",ll);
38069         std::fprintf(cimg::output(),"  ");
38070         _data[ll].print(tmp,display_stats);
38071         if (ll==3 && _width>8) { ll = _width-5; std::fprintf(cimg::output(),"  ...\n"); }
38072       }
38073       std::fflush(cimg::output());
38074       return *this;
38075     }
38076 
38077     //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
38078     /**
38079        This function displays the list images of the current CImgList instance into an existing CImgDisplay window.
38080        Images of the list are concatenated in a single temporarly image for visualization purposes.
38081        The function returns immediately.
38082        \param disp : reference to an existing CImgDisplay instance, where the current image list will be displayed.
38083        \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'c'.
38084        \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom).
38085        \return A reference to the current CImgList instance is returned.
38086     **/
38087     const CImgList<T>& display(CImgDisplay& disp, const char axis='x', const char align='p') const {
38088       get_append(axis,align).display(disp);
38089       return *this;
38090     }
38091 
38092     //! Display the current CImgList instance in a new display window.
38093     /**
38094        This function opens a new window with a specific title and displays the list images of the current CImgList instance into it.
38095        Images of the list are concatenated in a single temporarly image for visualization purposes.
38096        The function returns when a key is pressed or the display window is closed by the user.
38097        \param title : specify the title of the opening display window.
38098        \param axis : specify the axis for image concatenation. Can be 'x','y','z' or 'c'.
38099        \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom).
38100        \return A reference to the current CImgList instance is returned.
38101     **/
38102     const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
38103                                const char axis='x', const char align='p') const {
38104       if (is_empty())
38105         throw CImgInstanceException(_cimglist_instance
38106                                     "display() : Empty instance.",
38107                                     cimglist_instance);
38108 
38109       const CImg<T> visu = get_append(axis,align);
38110       if (display_info) print(disp.title());
38111       visu.display(disp,false);
38112       return *this;
38113     }
38114 
38115     //! Display the current CImgList instance in a new display window.
38116     const CImgList<T>& display(const char *const title=0, const bool display_info=true,
38117                                const char axis='x', const char align='p') const {
38118       const CImg<T> visu = get_append(axis,align);
38119       char ntitle[64] = { 0 };
38120       if (!title) std::sprintf(ntitle,"CImgList<%s>",pixel_type());
38121       if (display_info) print(title?title:ntitle);
38122       visu.display(title?title:ntitle,false);
38123       return *this;
38124     }
38125 
38126     //! Save an image list into a file.
38127     /**
38128        Depending on the extension of the given filename, a file format is chosen for the output file.
38129     **/
38130     const CImgList<T>& save(const char *const filename, const int number=-1) const {
38131       if (!filename)
38132         throw CImgArgumentException(_cimglist_instance
38133                                     "save() : Specified filename is (null).",
38134                                     cimglist_instance);
38135       // Do not test for empty instances, since .cimg format is able to manage empty instances.
38136       const char *const ext = cimg::split_filename(filename);
38137       char nfilename[1024] = { 0 };
38138       const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename;
38139 #ifdef cimglist_save_plugin
38140       cimglist_save_plugin(fn);
38141 #endif
38142 #ifdef cimglist_save_plugin1
38143       cimglist_save_plugin1(fn);
38144 #endif
38145 #ifdef cimglist_save_plugin2
38146       cimglist_save_plugin2(fn);
38147 #endif
38148 #ifdef cimglist_save_plugin3
38149       cimglist_save_plugin3(fn);
38150 #endif
38151 #ifdef cimglist_save_plugin4
38152       cimglist_save_plugin4(fn);
38153 #endif
38154 #ifdef cimglist_save_plugin5
38155       cimglist_save_plugin5(fn);
38156 #endif
38157 #ifdef cimglist_save_plugin6
38158       cimglist_save_plugin6(fn);
38159 #endif
38160 #ifdef cimglist_save_plugin7
38161       cimglist_save_plugin7(fn);
38162 #endif
38163 #ifdef cimglist_save_plugin8
38164       cimglist_save_plugin8(fn);
38165 #endif
38166       if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
38167       else if (!cimg::strcasecmp(ext,"cimg") || !ext[0]) return save_cimg(fn,false);
38168       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
38169       else if (!cimg::strcasecmp(ext,"avi") ||
38170                !cimg::strcasecmp(ext,"mov") ||
38171                !cimg::strcasecmp(ext,"asf") ||
38172                !cimg::strcasecmp(ext,"divx") ||
38173                !cimg::strcasecmp(ext,"flv") ||
38174                !cimg::strcasecmp(ext,"mpg") ||
38175                !cimg::strcasecmp(ext,"m1v") ||
38176                !cimg::strcasecmp(ext,"m2v") ||
38177                !cimg::strcasecmp(ext,"m4v") ||
38178                !cimg::strcasecmp(ext,"mjp") ||
38179                !cimg::strcasecmp(ext,"mkv") ||
38180                !cimg::strcasecmp(ext,"mpe") ||
38181                !cimg::strcasecmp(ext,"movie") ||
38182                !cimg::strcasecmp(ext,"ogm") ||
38183                !cimg::strcasecmp(ext,"qt") ||
38184                !cimg::strcasecmp(ext,"rm") ||
38185                !cimg::strcasecmp(ext,"vob") ||
38186                !cimg::strcasecmp(ext,"wmv") ||
38187                !cimg::strcasecmp(ext,"xvid") ||
38188                !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
38189 #ifdef cimg_use_tiff
38190       else if (!cimg::strcasecmp(ext,"tif") ||
38191           !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
38192 #endif
38193       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
38194       else { if (_width==1) _data[0].save(fn,-1); else cimglist_for(*this,l) _data[l].save(fn,l); }
38195       return *this;
38196     }
38197 
38198     // Tell if a CImgList can be saved as one single file.
38199     static bool is_saveable(const char *const filename) {
38200       const char *const ext = cimg::split_filename(filename);
38201       if (!cimg::strcasecmp(ext,"cimgz") ||
38202 #ifdef cimg_use_tiff
38203           !cimg::strcasecmp(ext,"tif") ||
38204           !cimg::strcasecmp(ext,"tiff") ||
38205 #endif
38206           !cimg::strcasecmp(ext,"yuv") ||
38207           !cimg::strcasecmp(ext,"avi") ||
38208           !cimg::strcasecmp(ext,"mov") ||
38209           !cimg::strcasecmp(ext,"asf") ||
38210           !cimg::strcasecmp(ext,"divx") ||
38211           !cimg::strcasecmp(ext,"flv") ||
38212           !cimg::strcasecmp(ext,"mpg") ||
38213           !cimg::strcasecmp(ext,"m1v") ||
38214           !cimg::strcasecmp(ext,"m2v") ||
38215           !cimg::strcasecmp(ext,"m4v") ||
38216           !cimg::strcasecmp(ext,"mjp") ||
38217           !cimg::strcasecmp(ext,"mkv") ||
38218           !cimg::strcasecmp(ext,"mpe") ||
38219           !cimg::strcasecmp(ext,"movie") ||
38220           !cimg::strcasecmp(ext,"ogm") ||
38221           !cimg::strcasecmp(ext,"qt") ||
38222           !cimg::strcasecmp(ext,"rm") ||
38223           !cimg::strcasecmp(ext,"vob") ||
38224           !cimg::strcasecmp(ext,"wmv") ||
38225           !cimg::strcasecmp(ext,"xvid") ||
38226           !cimg::strcasecmp(ext,"mpeg")) return true;
38227       return false;
38228     }
38229 
38230     //! Save an image sequence, using FFMPEG library.
38231     // This piece of code has been originally written by David. G. Starkweather.
38232     const CImgList<T>& save_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
38233                                    const unsigned int fps=25) const {
38234       if (!filename)
38235         throw CImgArgumentException(_cimglist_instance
38236                                     "save_ffmpeg() : Specified filename is (null).",
38237                                     cimglist_instance);
38238       if (is_empty())
38239         throw CImgInstanceException(_cimglist_instance
38240                                     "save_ffmpeg() : Empty instance, for file '%s'.",
38241                                     cimglist_instance,
38242                                     filename);
38243       if (!fps)
38244         throw CImgArgumentException(_cimglist_instance
38245                                     "save_ffmpeg() : Invalid specified framerate 0, for file '%s'.",
38246                                     cimglist_instance,
38247                                     filename);
38248 
38249       const unsigned int nlast_frame = last_frame==~0U?_width-1:last_frame;
38250       if (first_frame>=_width || nlast_frame>=_width)
38251         throw CImgArgumentException(_cimglist_instance
38252                                     "save_ffmpeg() : Out of range specified frames [%u,%u], for file '%s'.",
38253                                     cimglist_instance,
38254                                     first_frame,last_frame,filename);
38255 
38256       for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!_data[ll].is_sameXYZ(_data[0]))
38257         throw CImgInstanceException(_cimglist_instance
38258                                     "save_ffmpeg() : Invalid instance dimensions, for file '%s'.",
38259                                     cimglist_instance,
38260                                     filename);
38261 
38262 #ifndef cimg_use_ffmpeg
38263       return save_ffmpeg_external(filename,first_frame,last_frame);
38264 #else
38265       avcodec_register_all();
38266       av_register_all();
38267       const int
38268         frame_dimx = _data[first_frame].width(),
38269         frame_dimy = _data[first_frame].height(),
38270         frame_dimv = _data[first_frame].spectrum();
38271       if (frame_dimv!=1 && frame_dimv!=3)
38272         throw CImgInstanceException(_cimglist_instance
38273                                     "save_ffmpeg() : Image[0] (%u,%u,%u,%u,%p) has not 1 or 3 channels, for file '%s'.",
38274                                     cimglist_instance,
38275                                     _data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum,_data,filename);
38276 
38277       PixelFormat dest_pxl_fmt = PIX_FMT_YUV420P;
38278       PixelFormat src_pxl_fmt  = (frame_dimv == 3)?PIX_FMT_RGB24:PIX_FMT_GRAY8;
38279 
38280       int sws_flags = SWS_FAST_BILINEAR; // Interpolation method (keeping same size images for now).
38281       AVOutputFormat *fmt = 0;
38282       fmt = guess_format(0,filename,0);
38283       if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg".
38284       if (!fmt)
38285         throw CImgArgumentException(_cimglist_instance
38286                                     "save_ffmpeg() : Unable to determine codec for file '%s'.",
38287                                     cimglist_instance,
38288                                     filename);
38289 
38290       AVFormatContext *oc = 0;
38291 #ifdef LIBAVFORMAT_VERSION_MAJOR
38292       oc = avformat_alloc_context();
38293 #else
38294       oc = av_alloc_format_context();
38295 #endif
38296       if (!oc) // Failed to allocate format context.
38297         throw CImgIOException(_cimglist_instance
38298                               "save_ffmpeg() : Failed to allocate FFMPEG structure for format context, for file '%s'.",
38299                               cimglist_instance,
38300                               filename);
38301 
38302       AVCodec *codec = 0;
38303       AVFrame *picture = 0;
38304       AVFrame *tmp_pict = 0;
38305       oc->oformat = fmt;
38306       std::sprintf(oc->filename,"%s",filename);
38307 
38308       // Add video stream.
38309       int stream_index = 0;
38310       AVStream *video_str = 0;
38311       if (fmt->video_codec!=CODEC_ID_NONE) {
38312         video_str = av_new_stream(oc,stream_index);
38313         if (!video_str) { // Failed to allocate stream.
38314           av_free(oc);
38315           throw CImgIOException(_cimglist_instance
38316                                 "save_ffmpeg() : Failed to allocate FFMPEG structure for video stream, for file '%s'.",
38317                                 cimglist_instance,
38318                                 filename);
38319         }
38320       } else { // No codec identified.
38321         av_free(oc);
38322         throw CImgIOException(_cimglist_instance
38323                               "save_ffmpeg() : Failed to identify proper codec, for file '%s'.",
38324                               cimglist_instance,
38325                               filename);
38326       }
38327 
38328       AVCodecContext *c = video_str->codec;
38329       c->codec_id = fmt->video_codec;
38330       c->codec_type = CODEC_TYPE_VIDEO;
38331       c->bit_rate = 400000;
38332       c->width = frame_dimx;
38333       c->height = frame_dimy;
38334       c->time_base.num = 1;
38335       c->time_base.den = fps;
38336       c->gop_size = 12;
38337       c->pix_fmt = dest_pxl_fmt;
38338       if (c->codec_id == CODEC_ID_MPEG2VIDEO) c->max_b_frames = 2;
38339       if (c->codec_id == CODEC_ID_MPEG1VIDEO) c->mb_decision = 2;
38340 
38341       if (av_set_parameters(oc,0)<0) { // Parameters not properly set.
38342         av_free(oc);
38343         throw CImgIOException(_cimglist_instance
38344                               "save_ffmpeg() : Invalid parameters set for avcodec, for file '%s'.",
38345                               cimglist_instance,
38346                               filename);
38347       }
38348 
38349       // Open codecs and alloc buffers.
38350       codec = avcodec_find_encoder(c->codec_id);
38351       if (!codec) { // Failed to find codec.
38352         av_free(oc);
38353         throw CImgIOException(_cimglist_instance
38354                               "save_ffmpeg() : No valid codec found for file '%s'.",
38355                               cimglist_instance,
38356                               filename);
38357       }
38358       if (avcodec_open(c,codec)<0) // Failed to open codec.
38359         throw CImgIOException(_cimglist_instance
38360                               "save_ffmpeg() : Failed to open codec for file '%s'.",
38361                               cimglist_instance,
38362                               filename);
38363 
38364       tmp_pict = avcodec_alloc_frame();
38365       if (!tmp_pict) { // Failed to allocate memory for tmp_pict frame.
38366         avcodec_close(video_str->codec);
38367         av_free(oc);
38368         throw CImgIOException(_cimglist_instance
38369                               "save_ffmpeg() : Failed to allocate data buffer for file '%s'.",
38370                               cimglist_instance,
38371                               filename);
38372       }
38373       tmp_pict->linesize[0] = (src_pxl_fmt==PIX_FMT_RGB24)?3*frame_dimx:frame_dimx;
38374       tmp_pict->type = FF_BUFFER_TYPE_USER;
38375       int tmp_size = avpicture_get_size(src_pxl_fmt,frame_dimx,frame_dimy);
38376       uint8_t *tmp_buffer = (uint8_t*)av_malloc(tmp_size);
38377       if (!tmp_buffer) { // Failed to allocate memory for tmp buffer.
38378         av_free(tmp_pict);
38379         avcodec_close(video_str->codec);
38380         av_free(oc);
38381         throw CImgIOException(_cimglist_instance
38382                               "save_ffmpeg() : Failed to allocate data buffer for file '%s'.",
38383                               cimglist_instance,
38384                               filename);
38385       }
38386 
38387       // Associate buffer with tmp_pict.
38388       avpicture_fill((AVPicture*)tmp_pict,tmp_buffer,src_pxl_fmt,frame_dimx,frame_dimy);
38389       picture = avcodec_alloc_frame();
38390       if (!picture) { // Failed to allocate picture frame.
38391         av_free(tmp_pict->data[0]);
38392         av_free(tmp_pict);
38393         avcodec_close(video_str->codec);
38394         av_free(oc);
38395         throw CImgIOException(_cimglist_instance
38396                               "save_ffmpeg() : Failed to allocate data buffer for file '%s'.",
38397                               cimglist_instance,
38398                               filename);
38399       }
38400 
38401       int size = avpicture_get_size(c->pix_fmt,frame_dimx,frame_dimy);
38402       uint8_t *buffer = (uint8_t*)av_malloc(size);
38403       if (!buffer) { // Failed to allocate picture frame buffer.
38404         av_free(picture);
38405         av_free(tmp_pict->data[0]);
38406         av_free(tmp_pict);
38407         avcodec_close(video_str->codec);
38408         av_free(oc);
38409         throw CImgIOException(_cimglist_instance
38410                               "save_ffmpeg() : Failed to allocate data buffer for file '%s'.",
38411                               cimglist_instance,
38412                               filename);
38413       }
38414 
38415       // Associate the buffer with picture.
38416       avpicture_fill((AVPicture*)picture,buffer,c->pix_fmt,frame_dimx,frame_dimy);
38417 
38418       // Open file.
38419       if (!(fmt->flags&AVFMT_NOFILE)) {
38420         if (url_fopen(&oc->pb,filename,URL_WRONLY)<0)
38421           throw CImgIOException(_cimglist_instance
38422                                 "save_ffmpeg() : Failed to open file '%s'.",
38423                                 cimglist_instance,
38424                                 filename);
38425       }
38426 
38427       if (av_write_header(oc)<0)
38428         throw CImgIOException(_cimglist_instance
38429                               "save_ffmpeg() : Failed to write header in file '%s'.",
38430                               cimglist_instance,
38431                               filename);
38432 
38433       double video_pts;
38434       SwsContext *img_convert_context = 0;
38435       img_convert_context = sws_getContext(frame_dimx,frame_dimy,src_pxl_fmt,
38436                                            c->width,c->height,c->pix_fmt,sws_flags,0,0,0);
38437       if (!img_convert_context) { // Failed to get swscale context.
38438         // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
38439         av_free(picture->data);
38440         av_free(picture);
38441         av_free(tmp_pict->data[0]);
38442         av_free(tmp_pict);
38443         avcodec_close(video_str->codec);
38444         av_free(oc);
38445         throw CImgIOException(_cimglist_instance
38446                               "save_ffmpeg() : Failed to get conversion context for file '%s'.",
38447                               cimglist_instance,
38448                               filename);
38449       }
38450       int ret = 0, out_size;
38451       uint8_t *video_outbuf = 0;
38452       int video_outbuf_size = 1000000;
38453       video_outbuf = (uint8_t*)av_malloc(video_outbuf_size);
38454       if (!video_outbuf) {
38455         // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
38456         av_free(picture->data);
38457         av_free(picture);
38458         av_free(tmp_pict->data[0]);
38459         av_free(tmp_pict);
38460         avcodec_close(video_str->codec);
38461         av_free(oc);
38462         throw CImgIOException(_cimglist_instance
38463                               "save_ffmpeg() : Failed to allocate buffer memory, for file '%s'.",
38464                               cimglist_instance,
38465                               filename);
38466       }
38467 
38468       // Loop through each desired image in list.
38469       for (unsigned int i = first_frame; i<=nlast_frame; ++i) {
38470         CImg<uint8_t> currentIm = _data[i], red, green, blue, gray;
38471         if (src_pxl_fmt == PIX_FMT_RGB24) {
38472           red = currentIm.get_shared_channel(0);
38473           green = currentIm.get_shared_channel(1);
38474           blue = currentIm.get_shared_channel(2);
38475           cimg_forXY(currentIm,X,Y) { // Assign pizel values to data buffer in interlaced RGBRGB ... format.
38476             tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X]     = red(X,Y);
38477             tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 1] = green(X,Y);
38478             tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 2] = blue(X,Y);
38479           }
38480         } else {
38481           gray = currentIm.get_shared_channel(0);
38482           cimg_forXY(currentIm,X,Y) tmp_pict->data[0][Y*tmp_pict->linesize[0] + X] = gray(X,Y);
38483         }
38484 
38485         if (video_str) video_pts = (video_str->pts.val * video_str->time_base.num)/(video_str->time_base.den);
38486         else video_pts = 0.0;
38487         if (!video_str) break;
38488         if (sws_scale(img_convert_context,tmp_pict->data,tmp_pict->linesize,0,c->height,picture->data,picture->linesize)<0) break;
38489         out_size = avcodec_encode_video(c,video_outbuf,video_outbuf_size,picture);
38490         if (out_size>0) {
38491           AVPacket pkt;
38492           av_init_packet(&pkt);
38493           pkt.pts = av_rescale_q(c->coded_frame->pts,c->time_base,video_str->time_base);
38494           if (c->coded_frame->key_frame) pkt.flags|=PKT_FLAG_KEY;
38495           pkt.stream_index = video_str->index;
38496           pkt.data = video_outbuf;
38497           pkt.size = out_size;
38498           ret = av_write_frame(oc,&pkt);
38499         } else if (out_size<0) break;
38500         if (ret) break; // Error occured in writing frame.
38501       }
38502 
38503       // Close codec.
38504       if (video_str) {
38505         avcodec_close(video_str->codec);
38506         av_free(picture->data[0]);
38507         av_free(picture);
38508         av_free(tmp_pict->data[0]);
38509         av_free(tmp_pict);
38510       }
38511       if (av_write_trailer(oc)<0)
38512         throw CImgIOException(_cimglist_instance
38513                               "save_ffmpeg() : Failed to write trailer for file '%s'.",
38514                               cimglist_instance,
38515                               filename);
38516 
38517       av_freep(&oc->streams[stream_index]->codec);
38518       av_freep(&oc->streams[stream_index]);
38519       if (!(fmt->flags&AVFMT_NOFILE)) {
38520         /*if (url_fclose(oc->pb)<0)
38521           throw CImgIOException(_cimglist_instance
38522                                 "save_ffmpeg() : File '%s', failed to close file.",
38523                                 cimglist_instance,
38524                                 filename);
38525         */
38526       }
38527       av_free(oc);
38528       av_free(video_outbuf);
38529 #endif
38530       return *this;
38531     }
38532 
38533     // Save an image sequence into a YUV file (internal).
38534     const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename, const bool rgb2yuv) const {
38535       if (!file && !filename)
38536         throw CImgArgumentException(_cimglist_instance
38537                                     "save_yuv() : Specified filename is (null).",
38538                                     cimglist_instance);
38539       if (is_empty())
38540         throw CImgInstanceException(_cimglist_instance
38541                                     "save_yuv() : Empty instance, for file '%s'.",
38542                                     cimglist_instance,
38543                                     filename?filename:"(FILE*)");
38544 
38545       if ((*this)[0].width()%2 || (*this)[0].height()%2)
38546         throw CImgInstanceException(_cimglist_instance
38547                                     "save_yuv() : Invalid odd instance dimensions (%u,%u) for file '%s'.",
38548                                     cimglist_instance,
38549                                     (*this)[0].width(),(*this)[0].height(),
38550                                     filename?filename:"(FILE*)");
38551 
38552       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38553       cimglist_for(*this,l) {
38554         CImg<ucharT> YCbCr((*this)[l]);
38555         if (rgb2yuv) YCbCr.RGBtoYCbCr();
38556         cimg::fwrite(YCbCr._data,YCbCr._width*YCbCr._height,nfile);
38557         cimg::fwrite(YCbCr.get_resize(YCbCr._width/2, YCbCr._height/2,1,3,3).data(0,0,0,1),
38558                      YCbCr._width*YCbCr._height/2,nfile);
38559       }
38560       if (!file) cimg::fclose(nfile);
38561       return *this;
38562     }
38563 
38564     //! Save an image sequence into a YUV file.
38565     const CImgList<T>& save_yuv(const char *const filename=0, const bool rgb2yuv=true) const {
38566       return _save_yuv(0,filename,rgb2yuv);
38567     }
38568 
38569     //! Save an image sequence into a YUV file.
38570     const CImgList<T>& save_yuv(std::FILE *const file, const bool rgb2yuv=true) const {
38571       return _save_yuv(file,0,rgb2yuv);
38572     }
38573 
38574     //! Save an image list into a .cimg file.
38575     /**
38576        A CImg RAW file is a simple uncompressed binary file that may be used to save list of CImg<T> images.
38577        \param filename : name of the output file.
38578        \return A reference to the current CImgList instance is returned.
38579     **/
38580     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool compression) const {
38581       if (!file && !filename)
38582         throw CImgArgumentException(_cimglist_instance
38583                                     "save_cimg() : Specified filename is (null).",
38584                                     cimglist_instance);
38585 #ifndef cimg_use_zlib
38586       if (compression)
38587         cimg::warn(_cimglist_instance
38588                    "save_cimg() : Unable to save compressed data in file '%s' unless zlib is enabled, saving them uncompressed.",
38589                    cimglist_instance,
38590                    filename?filename:"(FILE*)");
38591 #endif
38592       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38593       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
38594       if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype+9,etype);
38595       else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
38596       cimglist_for(*this,l) {
38597         const CImg<T>& img = _data[l];
38598         std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
38599         if (img._data) {
38600           CImg<T> tmp;
38601           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
38602           const CImg<T>& ref = cimg::endianness()?tmp:img;
38603           bool compressed = false;
38604           if (compression) {
38605 #ifdef cimg_use_zlib
38606             const unsigned long siz = sizeof(T)*ref.size();
38607             unsigned long csiz = siz + siz/100 + 16;
38608             Bytef *const cbuf = new Bytef[csiz];
38609             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) {
38610               cimg::warn(_cimglist_instance
38611                          "save_cimg() : Failed to save compressed data for file '%s', saving them uncompressed.",
38612                          cimglist_instance,
38613                          filename?filename:"(FILE*)");
38614 
38615               compressed = false;
38616             } else {
38617               std::fprintf(nfile," #%lu\n",csiz);
38618               cimg::fwrite(cbuf,csiz,nfile);
38619               delete[] cbuf;
38620               compressed = true;
38621             }
38622 #else
38623             compressed = false;
38624 #endif
38625           }
38626           if (!compressed) {
38627             std::fputc('\n',nfile);
38628             cimg::fwrite(ref._data,ref.size(),nfile);
38629           }
38630         } else std::fputc('\n',nfile);
38631       }
38632       if (!file) cimg::fclose(nfile);
38633       return *this;
38634     }
38635 
38636     //! Save an image list into a CImg file (RAW binary file + simple header)
38637     const CImgList<T>& save_cimg(std::FILE *file, const bool compress=false) const {
38638       return _save_cimg(file,0,compress);
38639     }
38640 
38641     //! Save an image list into a CImg file (RAW binary file + simple header)
38642     const CImgList<T>& save_cimg(const char *const filename, const bool compress=false) const {
38643       return _save_cimg(0,filename,compress);
38644     }
38645 
38646     // Insert the instance image into into an existing .cimg file, at specified coordinates.
38647     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
38648                                  const unsigned int n0,
38649                                  const unsigned int x0, const unsigned int y0,
38650                                  const unsigned int z0, const unsigned int c0) const {
38651 #define _cimg_save_cimg_case(Ts,Tss) \
38652       if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
38653         for (unsigned int l = 0; l<lmax; ++l) { \
38654           j = 0; while((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
38655           W = H = D = C = 0; \
38656           if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
38657             throw CImgIOException(_cimglist_instance \
38658                                   "save_cimg() : Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
38659                                   cimglist_instance, \
38660                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
38661           if (W*H*D*C>0) { \
38662             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
38663             else { \
38664               const CImg<T>& img = (*this)[l - n0]; \
38665               const T *ptrs = img._data; \
38666               const unsigned int \
38667                 x1 = x0 + img._width - 1, \
38668                 y1 = y0 + img._height - 1, \
38669                 z1 = z0 + img._depth - 1, \
38670                 c1 = c0 + img._spectrum - 1, \
38671                 nx1 = x1>=W?W-1:x1, \
38672                 ny1 = y1>=H?H-1:y1, \
38673                 nz1 = z1>=D?D-1:z1, \
38674                 nc1 = c1>=C?C-1:c1; \
38675               CImg<Tss> raw(1+nx1-x0); \
38676               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
38677               if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
38678               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
38679                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
38680                 if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
38681                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
38682                   const unsigned int skipyb = y0*W*sizeof(Tss); \
38683                   if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
38684                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
38685                     const unsigned int skipxb = x0*sizeof(Tss); \
38686                     if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
38687                     raw.assign(ptrs, raw._width); \
38688                     ptrs+=img._width; \
38689                     if (endian) cimg::invert_endianness(raw._data,raw._width); \
38690                     cimg::fwrite(raw._data,raw._width,nfile); \
38691                     const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
38692                     if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
38693                   } \
38694                   const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
38695                   if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
38696                 } \
38697                 const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
38698                 if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
38699               } \
38700               const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
38701               if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
38702             } \
38703           } \
38704         } \
38705         saved = true; \
38706       }
38707 
38708       if (!file && !filename)
38709         throw CImgArgumentException(_cimglist_instance
38710                                     "save_cimg() : Specified filename is (null).",
38711                                     cimglist_instance);
38712       if (is_empty())
38713         throw CImgInstanceException(_cimglist_instance
38714                                     "save_cimg() : Empty instance, for file '%s'.",
38715                                     cimglist_instance,
38716                                     filename?filename:"(FILE*)");
38717 
38718       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
38719       bool saved = false, endian = cimg::endianness();
38720       char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
38721       unsigned int j, err, N, W, H, D, C;
38722       int i;
38723       j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
38724       err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
38725       if (err<2) {
38726         if (!file) cimg::fclose(nfile);
38727         throw CImgIOException(_cimglist_instance
38728                               "save_cimg() : CImg header not found in file '%s'.",
38729                               cimglist_instance,
38730                               filename?filename:"(FILE*)");
38731       }
38732       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
38733       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
38734       const unsigned int lmax = cimg::min(N,n0+_width);
38735       _cimg_save_cimg_case("bool",bool);
38736       _cimg_save_cimg_case("unsigned_char",unsigned char);
38737       _cimg_save_cimg_case("uchar",unsigned char);
38738       _cimg_save_cimg_case("char",char);
38739       _cimg_save_cimg_case("unsigned_short",unsigned short);
38740       _cimg_save_cimg_case("ushort",unsigned short);
38741       _cimg_save_cimg_case("short",short);
38742       _cimg_save_cimg_case("unsigned_int",unsigned int);
38743       _cimg_save_cimg_case("uint",unsigned int);
38744       _cimg_save_cimg_case("int",int);
38745       _cimg_save_cimg_case("unsigned_long",unsigned long);
38746       _cimg_save_cimg_case("ulong",unsigned long);
38747       _cimg_save_cimg_case("long",long);
38748       _cimg_save_cimg_case("float",float);
38749       _cimg_save_cimg_case("double",double);
38750       if (!saved) {
38751         if (!file) cimg::fclose(nfile);
38752         throw CImgIOException(_cimglist_instance
38753                               "save_cimg() : Unsupported data type '%s' for file '%s'.",
38754                               cimglist_instance,
38755                               filename?filename:"(FILE*)",str_pixeltype);
38756       }
38757       if (!file) cimg::fclose(nfile);
38758       return *this;
38759     }
38760 
38761     //! Insert the instance image into into an existing .cimg file, at specified coordinates.
38762     const CImgList<T>& save_cimg(const char *const filename,
38763                                  const unsigned int n0,
38764                                  const unsigned int x0, const unsigned int y0,
38765                                  const unsigned int z0, const unsigned int c0) const {
38766       return _save_cimg(0,filename,n0,x0,y0,z0,c0);
38767     }
38768 
38769     //! Insert the instance image into into an existing .cimg file, at specified coordinates.
38770     const CImgList<T>& save_cimg(std::FILE *const file,
38771                                  const unsigned int n0,
38772                                  const unsigned int x0, const unsigned int y0,
38773                                  const unsigned int z0, const unsigned int c0) const {
38774       return _save_cimg(file,0,n0,x0,y0,z0,c0);
38775     }
38776 
38777     // Create an empty .cimg file with specified dimensions (internal)
38778     static void _save_empty_cimg(std::FILE *const file, const char *const filename,
38779                                 const unsigned int nb,
38780                                 const unsigned int dx, const unsigned int dy,
38781                                 const unsigned int dz, const unsigned int dc) {
38782       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38783       const unsigned int siz = dx*dy*dz*dc*sizeof(T);
38784       std::fprintf(nfile,"%u %s\n",nb,pixel_type());
38785       for (unsigned int i=nb; i; --i) {
38786         std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
38787         for (unsigned int off=siz; off; --off) std::fputc(0,nfile);
38788       }
38789       if (!file) cimg::fclose(nfile);
38790     }
38791 
38792     //! Create an empty .cimg file with specified dimensions.
38793     static void save_empty_cimg(const char *const filename,
38794                                 const unsigned int nb,
38795                                 const unsigned int dx, const unsigned int dy=1,
38796                                 const unsigned int dz=1, const unsigned int dc=1) {
38797       return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
38798     }
38799 
38800     //! Create an empty .cimg file with specified dimensions.
38801     static void save_empty_cimg(std::FILE *const file,
38802                                 const unsigned int nb,
38803                                 const unsigned int dx, const unsigned int dy=1,
38804                                 const unsigned int dz=1, const unsigned int dc=1) {
38805       return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
38806     }
38807 
38808     //! Save a file in TIFF format.
38809 #ifdef cimg_use_tiff
38810     const CImgList<T>& save_tiff(const char *const filename) const {
38811       if (!filename)
38812         throw CImgArgumentException(_cimglist_instance
38813                                     "save_tiff() : Specified filename is (null).",
38814                                     cimglist_instance);
38815       if (is_empty())
38816         throw CImgInstanceException(_cimglist_instance
38817                                     "save_tiff() : Empty instance, for file '%s'.",
38818                                     cimglist_instance,
38819                                     filename);
38820 
38821       TIFF *tif = TIFFOpen(filename,"w");
38822       if (tif) {
38823         for (unsigned int dir = 0, l = 0; l<_width; ++l) {
38824           const CImg<T>& img = (*this)[l];
38825           if (img) {
38826             if (img._depth==1) img._save_tiff(tif,dir++);
38827             else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++);
38828           }
38829         }
38830         TIFFClose(tif);
38831       } else
38832         throw CImgException(_cimglist_instance
38833                             "save_tiff() : Failed to open stream for file '%s'.",
38834                             cimglist_instance,
38835                             filename);
38836       return *this;
38837     }
38838 #endif
38839 
38840     //! Save an image list as a gzipped file, using external tool 'gzip'.
38841     const CImgList<T>& save_gzip_external(const char *const filename) const {
38842       if (!filename)
38843         throw CImgIOException(_cimglist_instance
38844                               "save_gzip_external() : Specified filename is (null).",
38845                               cimglist_instance);
38846 
38847       char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
38848       const char
38849         *ext = cimg::split_filename(filename,body),
38850         *ext2 = cimg::split_filename(body,0);
38851       std::FILE *file;
38852       do {
38853         if (!cimg::strcasecmp(ext,"gz")) {
38854           if (*ext2) std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
38855           else std::sprintf(filetmp,"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
38856         } else {
38857           if (*ext) std::sprintf(filetmp,"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
38858           else std::sprintf(filetmp,"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
38859         }
38860         if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file);
38861       } while (file);
38862 
38863       if (is_saveable(body)) {
38864         save(filetmp);
38865         std::sprintf(command,"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename);
38866         cimg::system(command);
38867         file = std::fopen(filename,"rb");
38868         if (!file)
38869           throw CImgIOException(_cimglist_instance
38870                                 "save_gzip_external() : Failed to save file '%s' with external command 'gzip'.",
38871                                 cimglist_instance,
38872                                 filename);
38873         else cimg::fclose(file);
38874         std::remove(filetmp);
38875       } else {
38876         char nfilename[1024] = { 0 };
38877         cimglist_for(*this,l) {
38878           cimg::number_filename(body,l,6,nfilename);
38879           if (*ext) std::sprintf(nfilename+std::strlen(nfilename),".%s",ext);
38880           _data[l].save_gzip_external(nfilename);
38881         }
38882       }
38883       return *this;
38884     }
38885 
38886     //! Save an image sequence using the external tool 'ffmpeg'.
38887     const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
38888                                             const char *const codec="mpeg2video") const {
38889       if (!filename)
38890         throw CImgArgumentException(_cimglist_instance
38891                                     "save_ffmpeg_external() : Specified filename is (null).",
38892                                     cimglist_instance);
38893       if (is_empty())
38894         throw CImgInstanceException(_cimglist_instance
38895                                     "save_ffmpeg_external() : Empty instance, for file '%s'.",
38896                                     cimglist_instance,
38897                                     filename);
38898 
38899       char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
38900       std::FILE *file = 0;
38901       const unsigned int nlast_frame = last_frame==~0U?_width-1:last_frame;
38902       if (first_frame>=_width || nlast_frame>=_width)
38903         throw CImgArgumentException(_cimglist_instance
38904                                     "save_ffmpeg_external() : Out of range specified frames [%u,%u] for file '%s'.",
38905                                     cimglist_instance,
38906                                     filename,first_frame,last_frame);
38907 
38908       for (unsigned int ll = first_frame; ll<=nlast_frame; ++ll) if (!_data[ll].is_sameXYZ(_data[0]))
38909         throw CImgInstanceException(_cimglist_instance
38910                                     "save_ffmpeg_external() : Invalid instance dimensions for file '%s'.",
38911                                     cimglist_instance,
38912                                     filename);
38913       do {
38914         std::sprintf(filetmp,"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
38915         std::sprintf(filetmp2,"%s_000001.ppm",filetmp);
38916         if ((file=std::fopen(filetmp2,"rb"))!=0) std::fclose(file);
38917       } while (file);
38918       for (unsigned int l = first_frame; l<=nlast_frame; ++l) {
38919         std::sprintf(filetmp2,"%s_%.6u.ppm",filetmp,l+1);
38920         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filetmp2);
38921         else _data[l].save_pnm(filetmp2);
38922       }
38923 #if cimg_OS!=2
38924       std::sprintf(command,"ffmpeg -i %s_%%6d.ppm -vcodec %s -sameq -y \"%s\" >/dev/null 2>&1",filetmp,codec,filename);
38925 #else
38926       std::sprintf(command,"\"ffmpeg -i %s_%%6d.ppm -vcodec %s -sameq -y \"%s\"\" >NUL 2>&1",filetmp,codec,filename);
38927 #endif
38928 
38929       cimg::system(command);
38930       file = std::fopen(filename,"rb");
38931       if (!file)
38932         throw CImgIOException(_cimglist_instance
38933                               "save_ffmpeg_external() : Failed to save file '%s' with external command 'ffmpeg'.",
38934                               cimglist_instance,
38935                               filename);
38936 
38937       else cimg::fclose(file);
38938       cimglist_for(*this,lll) { std::sprintf(filetmp2,"%s_%.6u.ppm",filetmp,lll+1); std::remove(filetmp2); }
38939 
38940       return *this;
38941     }
38942 
38943     //@}
38944     //----------------------------------
38945     //
38946     //! \name Others
38947     //@{
38948     //----------------------------------
38949 
38950     //! Create an auto-cropped font (along the X axis) from a input font \p font.
38951     CImgList<T>& crop_font() {
38952       return get_crop_font().move_to(*this);
38953     }
38954 
38955     CImgList<T> get_crop_font() const {
38956       CImgList<T> res;
38957       cimglist_for(*this,l) {
38958         const CImg<T>& letter = (*this)[l];
38959         int xmin = letter._width, xmax = 0;
38960         cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; }
38961         if (xmin>xmax) CImg<T>(letter._width,letter._height,1,letter._spectrum,0).move_to(res);
38962         else letter.get_crop(xmin,0,xmax,letter._height-1).move_to(res);
38963       }
38964       res[' '].resize(res['f']._width,-100,-100,-100,0);
38965       res[' '+256].resize(res['f']._width,-100,-100,-100,0);
38966       return res;
38967     }
38968 
38969 
38970     //! Return a CImg pre-defined font with desired size.
38971     /**
38972        \param font_height = height of the desired font (exact match for 11,13,17,19,24,32,38,57)
38973        \param fixed_size = tell if the font has a fixed or variable width.
38974     **/
38975     static const CImgList<T>& font(const unsigned int font_height, const bool variable_size=true) {
38976 
38977 #define _cimg_font(sx,sy) \
38978     if (!variable_size && (!font || font[0]._height!=sy)) font = _font(cimg::font##sx##x##sy,sx,sy,false); \
38979     if (variable_size  && (!vfont || vfont[0]._height!=sy)) vfont = _font(cimg::font##sx##x##sy,sx,sy,true); \
38980     if (font_height==sy) return variable_size?vfont:font; \
38981     if (variable_size) { \
38982       if (cvfont && font_height==cvfont[0]._height) return cvfont; \
38983       cvfont = vfont; \
38984       cimglist_for(cvfont,l) \
38985         cvfont[l].resize(cimg::max(1U,cvfont[l]._width*font_height/cvfont[l]._height),font_height,-100,-100, \
38986                          cvfont[0]._height>font_height?2:5); \
38987       return cvfont; \
38988     } else { \
38989       if (cfont && font_height==cfont[0]._height) return cfont; \
38990       cfont = font; \
38991       cimglist_for(cfont,l) \
38992         cfont[l].resize(cimg::max(1U,cfont[l]._width*font_height/cfont[l]._height),font_height,-100,-100, \
38993                         cfont[0]._height>font_height?2:5); \
38994       return cfont; \
38995     } \
38996 
38997       static CImgList<T> font, vfont, cfont, cvfont;
38998       if (!font_height) return CImgList<T>::empty();
38999       if (font_height<=13) { _cimg_font(10,13); } // [1,13] -> ref 13
39000       if (font_height<=28) { _cimg_font(12,24); } // [14,28] -> ref 24
39001       if (font_height<=32) { _cimg_font(16,32); } // [29,32] -> ref 32
39002       _cimg_font(29,57);                          // [33,+inf] -> ref 57
39003     }
39004 
39005     static CImgList<T> _font(const unsigned int *const font, const unsigned int w, const unsigned int h, const bool variable_size) {
39006       CImgList<T> res = CImgList<T>(256,w,h,1,3);
39007       res.insert(256); cimglist_for_in(res,256,511,l) res[l].assign(w,h,1,1);
39008       const unsigned int *ptr = font;
39009       unsigned int m = 0, val = 0;
39010       for (unsigned int y = 0; y<h; ++y)
39011         for (unsigned int x = 0; x<256*w; ++x) {
39012           m>>=1; if (!m) { m = 0x80000000; val = *(ptr++); }
39013           CImg<T>& img = res[x/w], &mask = res[x/w+256];
39014           unsigned int xm = x%w;
39015           img(xm,y,0) = img(xm,y,1) = img(xm,y,2) = mask(xm,y,0) = (T)((val&m)?1:0);
39016         }
39017       if (variable_size) res.crop_font();
39018       return res;
39019     }
39020 
39021     //! Compute a 1-D Fast Fourier Transform, along specified axis.
39022     CImgList<T>& FFT(const char axis, const bool invert=false) {
39023       if (is_empty()) return *this;
39024       if (_width==1) insert(1);
39025       if (_width>2)
39026         cimg::warn(_cimglist_instance
39027                    "FFT() : Instance has more than 2 images",
39028                    cimglist_instance);
39029 
39030       CImg<T>::FFT(_data[0],_data[1],axis,invert);
39031       return *this;
39032     }
39033 
39034     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
39035       return CImgList<Tfloat>(*this,false).FFT(axis,invert);
39036     }
39037 
39038     //! Compute a n-d Fast Fourier Transform.
39039     CImgList<T>& FFT(const bool invert=false) {
39040       if (is_empty()) return *this;
39041       if (_width==1) insert(1);
39042       if (_width>2)
39043         cimg::warn(_cimglist_instance
39044                    "FFT() : Instance has more than 2 images",
39045                    cimglist_instance);
39046 
39047       CImg<T>::FFT(_data[0],_data[1],invert);
39048       return *this;
39049     }
39050 
39051     CImgList<Tfloat> get_FFT(const bool invert=false) const {
39052       return CImgList<Tfloat>(*this,false).FFT(invert);
39053     }
39054 
39055     //! Invert primitives orientation of a 3d object.
39056     CImgList<T>& reverse_object3d() {
39057       cimglist_for(*this,l) {
39058         CImg<T>& p = _data[l];
39059         const unsigned int siz = p.size();
39060         if (siz==2 || siz==3 || siz==6 || siz==9) cimg::swap(p[0],p[1]);
39061         else if (siz==4 || siz==12) cimg::swap(p[0],p[3],p[1],p[2]);
39062       }
39063       return *this;
39064     }
39065 
39066     CImgList<T> get_reverse_object3d() const {
39067       return (+*this).reverse_object3d();
39068     }
39069 
39070     //@}
39071   };
39072 
39073   /*
39074   #---------------------------------------------
39075   #
39076    # Completion of previously declared functions
39077    #
39078    #----------------------------------------------
39079   */
39080 
39081 namespace cimg {
39082 
39083   //! Display a dialog box, where a user can click standard buttons.
39084   /**
39085      Up to 6 buttons can be defined in the dialog window.
39086      This function returns when a user clicked one of the button or closed the dialog window.
39087      \param title = Title of the dialog window.
39088      \param msg = Main message displayed inside the dialog window.
39089      \param button1_label = Label of the 1st button.
39090      \param button2_label = Label of the 2nd button.
39091      \param button3_label = Label of the 3rd button.
39092      \param button4_label = Label of the 4th button.
39093      \param button5_label = Label of the 5th button.
39094      \param button6_label = Label of the 6th button.
39095      \param logo = Logo image displayed at the left of the main message. This parameter is optional.
39096      \param centering = Tell to center the dialog window on the screen.
39097      \return The button number (from 0 to 5), or -1 if the dialog window has been closed by the user.
39098      \note If a button text is set to 0, then the corresponding button (and the followings) won't appear in
39099      the dialog box. At least one button is necessary.
39100   **/
39101   template<typename t>
39102   inline int dialog(const char *const title, const char *const msg,
39103                     const char *const button1_label, const char *const button2_label,
39104                     const char *const button3_label, const char *const button4_label,
39105                     const char *const button5_label, const char *const button6_label,
39106                     const CImg<t>& logo, const bool centering = false) {
39107 #if cimg_display==0
39108     cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,logo._data,centering);
39109     throw CImgDisplayException("cimg::dialog() : No display available.");
39110 #else
39111     const unsigned char
39112       black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
39113 
39114     // Create buttons and canvas graphics
39115     CImgList<unsigned char> buttons, cbuttons, sbuttons;
39116     if (button1_label) { CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
39117       if (button2_label) { CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
39118         if (button3_label) { CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
39119           if (button4_label) { CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
39120             if (button5_label) { CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
39121               if (button6_label) { CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
39122               }}}}}}
39123     if (!buttons._width)
39124       throw CImgArgumentException("cimg::dialog() : No buttons have been defined.");
39125 
39126     unsigned int bw = 0, bh = 0;
39127     cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l]._width); bh = cimg::max(bh,buttons[l]._height); }
39128     bw+=8; bh+=8;
39129     if (bw<64) bw=64;
39130     if (bw>128) bw=128;
39131     if (bh<24) bh=24;
39132     if (bh>48) bh=48;
39133 
39134     CImg<unsigned char> button(bw,bh,1,3);
39135     button.draw_rectangle(0,0,bw-1,bh-1,gray);
39136     button.draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white);
39137     button.draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black);
39138     button.draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2);
39139     CImg<unsigned char> sbutton(bw,bh,1,3);
39140     sbutton.draw_rectangle(0,0,bw-1,bh-1,gray);
39141     sbutton.draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black);
39142     sbutton.draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black);
39143     sbutton.draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white);
39144     sbutton.draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black);
39145     sbutton.draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2);
39146     sbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false);
39147     sbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false);
39148     CImg<unsigned char> cbutton(bw,bh,1,3);
39149     cbutton.draw_rectangle(0,0,bw-1,bh-1,black).draw_rectangle(1,1,bw-2,bh-2,gray2).draw_rectangle(2,2,bw-3,bh-3,gray);
39150     cbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false);
39151     cbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false);
39152 
39153     cimglist_for(buttons,ll) {
39154       CImg<unsigned char>(cbutton).draw_image(1+(bw-buttons[ll].width())/2,1+(bh-buttons[ll].height())/2,buttons[ll]).
39155         move_to(cbuttons);
39156       CImg<unsigned char>(sbutton).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
39157         move_to(sbuttons);
39158       CImg<unsigned char>(button).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
39159         move_to(buttons[ll]);
39160     }
39161 
39162     CImg<unsigned char> canvas;
39163     if (msg) CImg<unsigned char>().draw_text(0,0,"%s",black,gray,1,13,msg).move_to(canvas);
39164     const unsigned int
39165       bwall = (buttons._width-1)*(12+bw) + bw,
39166       w = cimg::max(196U,36+logo._width+canvas._width,24+bwall),
39167       h = cimg::max(96U,36+canvas._height+bh,36+logo._height+bh),
39168       lx = 12 + (canvas._data?0:((w-24-logo._width)/2)),
39169       ly = (h-12-bh-logo._height)/2,
39170       tx = lx+logo._width+12,
39171       ty = (h-12-bh-canvas._height)/2,
39172       bx = (w-bwall)/2,
39173       by = h-12-bh;
39174 
39175     if (canvas._data)
39176       canvas = CImg<unsigned char>(w,h,1,3).
39177         draw_rectangle(0,0,w-1,h-1,gray).
39178         draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
39179         draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black).
39180         draw_image(tx,ty,canvas);
39181     else
39182       canvas = CImg<unsigned char>(w,h,1,3).
39183         draw_rectangle(0,0,w-1,h-1,gray).
39184         draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
39185         draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black);
39186     if (logo._data) canvas.draw_image(lx,ly,logo);
39187 
39188     unsigned int xbuttons[6] = { 0 };
39189     cimglist_for(buttons,lll) { xbuttons[lll] = bx+(bw+12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); }
39190 
39191     // Open window and enter events loop
39192     CImgDisplay disp(canvas,title?title:" ",0,false,centering?true:false);
39193     if (centering) disp.move((CImgDisplay::screen_width() - disp.width())/2,
39194                              (CImgDisplay::screen_height() - disp.height())/2);
39195     bool stopflag = false, refresh = false;
39196     int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
39197     while (!disp.is_closed() && !stopflag) {
39198       if (refresh) {
39199         if (clicked>=0) CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
39200         else {
39201           if (selected>=0) CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
39202           else canvas.display(disp);
39203         }
39204         refresh = false;
39205       }
39206       disp.wait(15);
39207       if (disp.is_resized()) disp.resize(disp);
39208 
39209       if (disp.button()&1)  {
39210         oclicked = clicked;
39211         clicked = -1;
39212         cimglist_for(buttons,l)
39213           if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by+bh) &&
39214               disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l]+bw)) {
39215             clicked = selected = l;
39216             refresh = true;
39217           }
39218         if (clicked!=oclicked) refresh = true;
39219       } else if (clicked>=0) stopflag = true;
39220 
39221       if (disp.key()) {
39222         oselected = selected;
39223         switch (disp.key()) {
39224         case cimg::keyESC : selected=-1; stopflag=true; break;
39225         case cimg::keyENTER : if (selected<0) selected = 0; stopflag = true; break;
39226         case cimg::keyTAB :
39227         case cimg::keyARROWRIGHT :
39228         case cimg::keyARROWDOWN : selected = (selected+1)%buttons._width; break;
39229         case cimg::keyARROWLEFT :
39230         case cimg::keyARROWUP : selected = (selected+buttons._width-1)%buttons._width; break;
39231         }
39232         disp.set_key();
39233         if (selected!=oselected) refresh = true;
39234       }
39235     }
39236     if (!disp) selected = -1;
39237     return selected;
39238 #endif
39239   }
39240 
39241   inline int dialog(const char *const title, const char *const msg,
39242                     const char *const button1_label, const char *const button2_label, const char *const button3_label,
39243                     const char *const button4_label, const char *const button5_label, const char *const button6_label,
39244                     const bool centering) {
39245     return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
39246                   CImg<unsigned char>::logo40x38(),centering);
39247   }
39248 
39249   //! Evaluate math expression.
39250   inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
39251     static const CImg<float> empty;
39252     return empty.eval(expression,x,y,z,c);
39253   }
39254 
39255   // End of cimg:: namespace
39256 }
39257 
39258   // End of cimg_library:: namespace
39259 }
39260 
39261 #ifdef _cimg_redefine_None
39262 #define None 0
39263 #endif
39264 #ifdef _cimg_redefine_min
39265 #define min(a,b) (((a)<(b))?(a):(b))
39266 #endif
39267 #ifdef _cimg_redefine_max
39268 #define max(a,b) (((a)>(b))?(a):(b))
39269 #endif
39270 #ifdef _cimg_redefine_PI
39271 #define PI 3.141592653589793238462643383
39272 #endif
39273 
39274 #endif
39275 // Local Variables:
39276 // mode: c++
39277 // End:
Generated on Sun May 8 08:42:06 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3