1 /*
2  #
3  #  File            : CImg.h
4  #                    ( C++ header file )
5  #
6  #  Description     : The C++ Template Image Processing Toolkit.
7  #                    This file is the main component of the CImg Library project.
8  #                    ( http://cimg.eu )
9  #
10  #  Project manager : David Tschumperle.
11  #                    ( http://tschumperle.users.greyc.fr/ )
12  #
13  #                    A complete list of contributors is available in file 'README.txt'
14  #                    distributed within the CImg package.
15  #
16  #  Licenses        : This file is 'dual-licensed', you have to choose one
17  #                    of the two licenses below to apply.
18  #
19  #                    CeCILL-C
20  #                    The CeCILL-C license is close to the GNU LGPL.
21  #                    ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
22  #
23  #                or  CeCILL v2.1
24  #                    The CeCILL license is compatible with the GNU GPL.
25  #                    ( http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html )
26  #
27  #  This software is governed either by the CeCILL or the CeCILL-C license
28  #  under French law and abiding by the rules of distribution of free software.
29  #  You can  use, modify and or redistribute the software under the terms of
30  #  the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA
31  #  at the following URL: "http://www.cecill.info".
32  #
33  #  As a counterpart to the access to the source code and  rights to copy,
34  #  modify and redistribute granted by the license, users are provided only
35  #  with a limited warranty  and the software's author,  the holder of the
36  #  economic rights,  and the successive licensors  have only  limited
37  #  liability.
38  #
39  #  In this respect, the user's attention is drawn to the risks associated
40  #  with loading,  using,  modifying and/or developing or reproducing the
41  #  software by the user in light of its specific status of free software,
42  #  that may mean  that it is complicated to manipulate,  and  that  also
43  #  therefore means  that it is reserved for developers  and  experienced
44  #  professionals having in-depth computer knowledge. Users are therefore
45  #  encouraged to load and test the software's suitability as regards their
46  #  requirements in conditions enabling the security of their systems and/or
47  #  data to be ensured and,  more generally, to use and operate it in the
48  #  same conditions as regards security.
49  #
50  #  The fact that you are presently reading this means that you have had
51  #  knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms.
52  #
53 */
54 
55 // Set version number of the library.
56 #ifndef cimg_version
57 #define cimg_version 233
58 
59 /*-----------------------------------------------------------
60  #
61  # Test and possibly auto-set CImg configuration variables
62  # and include required headers.
63  #
64  # If you find that the default configuration variables are
65  # not adapted to your system, you can override their values
66  # before including the header file "CImg.h"
67  # (use the #define directive).
68  #
69  ------------------------------------------------------------*/
70 
71 // Include standard C++ headers.
72 // This is the minimal set of required headers to make CImg-based codes compile.
73 #include <cstdio>
74 #include <cstdlib>
75 #include <cstdarg>
76 #include <cstring>
77 #include <cmath>
78 #include <cfloat>
79 #include <climits>
80 #include <ctime>
81 #include <exception>
82 #include <algorithm>
83 
84 // Detect/configure OS variables.
85 //
86 // Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies).
87 //                      '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
88 //                      '2' for Microsoft Windows.
89 //                      (auto-detection is performed if 'cimg_OS' is not set by the user).
90 #ifndef cimg_OS
91 #if defined(unix)        || defined(__unix)      || defined(__unix__) \
92  || defined(linux)       || defined(__linux)     || defined(__linux__) \
93  || defined(sun)         || defined(__sun) \
94  || defined(BSD)         || defined(__OpenBSD__) || defined(__NetBSD__) \
95  || defined(__FreeBSD__) || defined (__DragonFly__) \
96  || defined(sgi)         || defined(__sgi) \
97  || defined(__MACOSX__)  || defined(__APPLE__) \
98  || defined(__CYGWIN__)
99 #define cimg_OS 1
100 #elif defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) \
101    || defined(WIN64)    || defined(_WIN64) || defined(__WIN64__)
102 #define cimg_OS 2
103 #else
104 #define cimg_OS 0
105 #endif
106 #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
107 #error CImg Library: Invalid configuration variable 'cimg_OS'.
108 #error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
109 #endif
110 #ifndef cimg_date
111 #define cimg_date __DATE__
112 #endif
113 #ifndef cimg_time
114 #define cimg_time __TIME__
115 #endif
116 
117 // Disable silly warnings on some Microsoft VC++ compilers.
118 #ifdef _MSC_VER
119 #pragma warning(push)
120 #pragma warning(disable:4127)
121 #pragma warning(disable:4244)
122 #pragma warning(disable:4311)
123 #pragma warning(disable:4312)
124 #pragma warning(disable:4319)
125 #pragma warning(disable:4512)
126 #pragma warning(disable:4571)
127 #pragma warning(disable:4640)
128 #pragma warning(disable:4706)
129 #pragma warning(disable:4710)
130 #pragma warning(disable:4800)
131 #pragma warning(disable:4804)
132 #pragma warning(disable:4820)
133 #pragma warning(disable:4996)
134 
135 #ifndef _CRT_SECURE_NO_DEPRECATE
136 #define _CRT_SECURE_NO_DEPRECATE 1
137 #endif
138 #ifndef _CRT_SECURE_NO_WARNINGS
139 #define _CRT_SECURE_NO_WARNINGS 1
140 #endif
141 #ifndef _CRT_NONSTDC_NO_DEPRECATE
142 #define _CRT_NONSTDC_NO_DEPRECATE 1
143 #endif
144 #endif
145 
146 // Define correct string functions for each compiler and OS.
147 #if cimg_OS==2 && defined(_MSC_VER)
148 #define cimg_sscanf std::sscanf
149 #define cimg_sprintf std::sprintf
150 #define cimg_snprintf cimg::_snprintf
151 #define cimg_vsnprintf cimg::_vsnprintf
152 #else
153 #include <stdio.h>
154 #if defined(__MACOSX__) || defined(__APPLE__)
155 #define cimg_sscanf cimg::_sscanf
156 #define cimg_sprintf cimg::_sprintf
157 #define cimg_snprintf cimg::_snprintf
158 #define cimg_vsnprintf cimg::_vsnprintf
159 #else
160 #define cimg_sscanf std::sscanf
161 #define cimg_sprintf std::sprintf
162 #define cimg_snprintf snprintf
163 #define cimg_vsnprintf vsnprintf
164 #endif
165 #endif
166 
167 // Include OS-specific headers.
168 #if cimg_OS==1
169 #include <sys/types.h>
170 #include <sys/time.h>
171 #include <sys/stat.h>
172 #include <unistd.h>
173 #include <dirent.h>
174 #include <fnmatch.h>
175 #elif cimg_OS==2
176 #ifndef std_fopen
177 #define std_fopen cimg::win_fopen
178 #endif
179 #ifndef NOMINMAX
180 #define NOMINMAX
181 #endif
182 #ifndef WIN32_LEAN_AND_MEAN
183 #define WIN32_LEAN_AND_MEAN
184 #endif
185 #include <windows.h>
186 #ifndef _WIN32_IE
187 #define _WIN32_IE 0x0400
188 #endif
189 #include <shlobj.h>
190 #include <process.h>
191 #include <io.h>
192 #endif
193 
194 // Look for C++11 features.
195 #ifndef cimg_use_cpp11
196 #if __cplusplus>201100
197 #define cimg_use_cpp11 1
198 #else
199 #define cimg_use_cpp11 0
200 #endif
201 #endif
202 #if cimg_use_cpp11==1
203 #include <initializer_list>
204 #include <utility>
205 #endif
206 
207 // Convenient macro to define pragma
208 #ifdef _MSC_VER
209 #define cimg_pragma(x) __pragma(x)
210 #else
211 #define cimg_pragma(x) _Pragma(#x)
212 #endif
213 
214 // Define own types 'cimg_long/ulong' and 'cimg_int64/uint64' to ensure portability.
215 // ( constrained to 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' and 'sizeof(cimg_int64/cimg_uint64)=8' ).
216 #if cimg_OS==2
217 
218 #define cimg_uint64 unsigned __int64
219 #define cimg_int64 __int64
220 #define cimg_ulong UINT_PTR
221 #define cimg_long INT_PTR
222 #ifdef _MSC_VER
223 #define cimg_fuint64 "%I64u"
224 #define cimg_fint64 "%I64d"
225 #else
226 #define cimg_fuint64 "%llu"
227 #define cimg_fint64 "%lld"
228 #endif
229 
230 #else
231 
232 #if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))
233 #define cimg_uint64 unsigned long long
234 #define cimg_int64 long long
235 #define cimg_fuint64 "%llu"
236 #define cimg_fint64 "%lld"
237 #else
238 #define cimg_uint64 unsigned long
239 #define cimg_int64 long
240 #define cimg_fuint64 "%lu"
241 #define cimg_fint64 "%ld"
242 #endif
243 
244 #if defined(__arm__) || defined(_M_ARM)
245 #define cimg_ulong unsigned long long
246 #define cimg_long long long
247 #else
248 #define cimg_ulong unsigned long
249 #define cimg_long long
250 #endif
251 
252 #endif
253 
254 // Configure filename separator.
255 //
256 // Filename separator is set by default to '/', except for Windows where it is '\'.
257 #ifndef cimg_file_separator
258 #if cimg_OS==2
259 #define cimg_file_separator '\\'
260 #else
261 #define cimg_file_separator '/'
262 #endif
263 #endif
264 
265 // Configure verbosity of output messages.
266 //
267 // Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode).
268 //                             '1' to output library messages on the console.
269 //                             '2' to output library messages on a basic dialog window (default behavior).
270 //                             '3' to do as '1' + add extra warnings (may slow down the code!).
271 //                             '4' to do as '2' + add extra warnings (may slow down the code!).
272 //
273 // Define 'cimg_strict_warnings' to replace warning messages by exception throwns.
274 //
275 // Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals.
276 #ifndef cimg_verbosity
277 #if cimg_OS==2
278 #define cimg_verbosity 2
279 #else
280 #define cimg_verbosity 1
281 #endif
282 #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4)
283 #error CImg Library: Configuration variable 'cimg_verbosity' is badly defined.
284 #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }).
285 #endif
286 
287 // Configure display framework.
288 //
289 // Define 'cimg_display' to: '0' to disable display capabilities.
290 //                           '1' to use the X-Window framework (X11).
291 //                           '2' to use the Microsoft GDI32 framework.
292 #ifndef cimg_display
293 #if cimg_OS==0
294 #define cimg_display 0
295 #elif cimg_OS==1
296 #define cimg_display 1
297 #elif cimg_OS==2
298 #define cimg_display 2
299 #endif
300 #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
301 #error CImg Library: Configuration variable 'cimg_display' is badly defined.
302 #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
303 #endif
304 
305 // Configure the 'abort' signal handler (does nothing by default).
306 // A typical signal handler can be defined in your own source like this:
307 // #define cimg_abort_test if (is_abort) throw CImgAbortException("")
308 //
309 // where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method.
310 // 'cimg_abort_test2' does the same but is called more often (in inner loops).
311 #if defined(cimg_abort_test) && defined(cimg_use_openmp)
312 
313 // Define abort macros to be used with OpenMP.
314 #ifndef _cimg_abort_init_omp
315 #define _cimg_abort_init_omp bool _cimg_abort_go_omp = true; cimg::unused(_cimg_abort_go_omp)
316 #endif
317 #ifndef _cimg_abort_try_omp
318 #define _cimg_abort_try_omp if (_cimg_abort_go_omp) try
319 #endif
320 #ifndef _cimg_abort_catch_omp
321 #define _cimg_abort_catch_omp catch (CImgAbortException&) { cimg_pragma(omp atomic) _cimg_abort_go_omp&=false; }
322 #endif
323 #ifdef cimg_abort_test2
324 #ifndef _cimg_abort_try_omp2
325 #define _cimg_abort_try_omp2 _cimg_abort_try_omp
326 #endif
327 #ifndef _cimg_abort_catch_omp2
328 #define _cimg_abort_catch_omp2 _cimg_abort_catch_omp
329 #endif
330 #ifndef _cimg_abort_catch_fill_omp
331 #define _cimg_abort_catch_fill_omp \
332   catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg<charT>::string(e._message).move_to(is_error); \
333                              cimg_pragma(omp atomic) _cimg_abort_go_omp&=false; }
334 #endif
335 #endif
336 #endif
337 
338 #ifndef _cimg_abort_init_omp
339 #define _cimg_abort_init_omp
340 #endif
341 #ifndef _cimg_abort_try_omp
342 #define _cimg_abort_try_omp
343 #endif
344 #ifndef _cimg_abort_catch_omp
345 #define _cimg_abort_catch_omp
346 #endif
347 #ifndef _cimg_abort_try_omp2
348 #define _cimg_abort_try_omp2
349 #endif
350 #ifndef _cimg_abort_catch_omp2
351 #define _cimg_abort_catch_omp2
352 #endif
353 #ifndef _cimg_abort_catch_fill_omp
354 #define _cimg_abort_catch_fill_omp
355 #endif
356 #ifndef cimg_abort_init
357 #define cimg_abort_init
358 #endif
359 #ifndef cimg_abort_test
360 #define cimg_abort_test
361 #endif
362 #ifndef cimg_abort_test2
363 #define cimg_abort_test2
364 #endif
365 #ifndef std_fopen
366 #define std_fopen std::fopen
367 #endif
368 
369 // Include display-specific headers.
370 #if cimg_display==1
371 #include <X11/Xlib.h>
372 #include <X11/Xutil.h>
373 #include <X11/keysym.h>
374 #include <pthread.h>
375 #ifdef cimg_use_xshm
376 #include <sys/ipc.h>
377 #include <sys/shm.h>
378 #include <X11/extensions/XShm.h>
379 #endif
380 #ifdef cimg_use_xrandr
381 #include <X11/extensions/Xrandr.h>
382 #endif
383 #endif
384 #ifndef cimg_appname
385 #define cimg_appname "CImg"
386 #endif
387 
388 // Configure OpenMP support.
389 // (http://www.openmp.org)
390 //
391 // Define 'cimg_use_openmp' to enable OpenMP support.
392 //
393 // OpenMP directives may be used in a (very) few CImg functions to get
394 // advantages of multi-core CPUs.
395 #ifdef cimg_use_openmp
396 #include <omp.h>
397 #define cimg_pragma_openmp(p) cimg_pragma(omp p)
398 #else
399 #define cimg_pragma_openmp(p)
400 #endif
401 
402 // Configure OpenCV support.
403 // (http://opencv.willowgarage.com/wiki/)
404 //
405 // Define 'cimg_use_opencv' to enable OpenCV support.
406 //
407 // OpenCV library may be used to access images from cameras
408 // (see method 'CImg<T>::load_camera()').
409 #ifdef cimg_use_opencv
410 #ifdef True
411 #undef True
412 #define _cimg_redefine_True
413 #endif
414 #ifdef False
415 #undef False
416 #define _cimg_redefine_False
417 #endif
418 #include <cstddef>
419 #include "cv.h"
420 #include "highgui.h"
421 #endif
422 
423 // Configure LibPNG support.
424 // (http://www.libpng.org)
425 //
426 // Define 'cimg_use_png' to enable LibPNG support.
427 //
428 // PNG library may be used to get a native support of '.png' files.
429 // (see methods 'CImg<T>::{load,save}_png()'.
430 #ifdef cimg_use_png
431 extern "C" {
432 #include "png.h"
433 }
434 #endif
435 
436 // Configure LibJPEG support.
437 // (http://en.wikipedia.org/wiki/Libjpeg)
438 //
439 // Define 'cimg_use_jpeg' to enable LibJPEG support.
440 //
441 // JPEG library may be used to get a native support of '.jpg' files.
442 // (see methods 'CImg<T>::{load,save}_jpeg()').
443 #ifdef cimg_use_jpeg
444 extern "C" {
445 #include "jpeglib.h"
446 #include "setjmp.h"
447 }
448 #endif
449 
450 // Configure LibTIFF support.
451 // (http://www.libtiff.org)
452 //
453 // Define 'cimg_use_tiff' to enable LibTIFF support.
454 //
455 // TIFF library may be used to get a native support of '.tif' files.
456 // (see methods 'CImg[List]<T>::{load,save}_tiff()').
457 #ifdef cimg_use_tiff
458 extern "C" {
459 #define uint64 uint64_hack_
460 #define int64 int64_hack_
461 #include "tiffio.h"
462 #undef uint64
463 #undef int64
464 }
465 #endif
466 
467 // Configure LibMINC2 support.
468 // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference)
469 //
470 // Define 'cimg_use_minc2' to enable LibMINC2 support.
471 //
472 // MINC2 library may be used to get a native support of '.mnc' files.
473 // (see methods 'CImg<T>::{load,save}_minc2()').
474 #ifdef cimg_use_minc2
475 #include "minc_io_simple_volume.h"
476 #include "minc_1_simple.h"
477 #include "minc_1_simple_rw.h"
478 #endif
479 
480 // Configure Zlib support.
481 // (http://www.zlib.net)
482 //
483 // Define 'cimg_use_zlib' to enable Zlib support.
484 //
485 // Zlib library may be used to allow compressed data in '.cimgz' files
486 // (see methods 'CImg[List]<T>::{load,save}_cimg()').
487 #ifdef cimg_use_zlib
488 extern "C" {
489 #include "zlib.h"
490 }
491 #endif
492 
493 // Configure libcurl support.
494 // (http://curl.haxx.se/libcurl/)
495 //
496 // Define 'cimg_use_curl' to enable libcurl support.
497 //
498 // Libcurl may be used to get a native support of file downloading from the network.
499 // (see method 'cimg::load_network()'.)
500 #ifdef cimg_use_curl
501 #include "curl/curl.h"
502 #endif
503 
504 // Configure Magick++ support.
505 // (http://www.imagemagick.org/Magick++)
506 //
507 // Define 'cimg_use_magick' to enable Magick++ support.
508 //
509 // Magick++ library may be used to get a native support of various image file formats.
510 // (see methods 'CImg<T>::{load,save}()').
511 #ifdef cimg_use_magick
512 #include "Magick++.h"
513 #endif
514 
515 // Configure FFTW3 support.
516 // (http://www.fftw.org)
517 //
518 // Define 'cimg_use_fftw3' to enable libFFTW3 support.
519 //
520 // FFTW3 library may be used to efficiently compute the Fast Fourier Transform
521 // of image data, without restriction on the image size.
522 // (see method 'CImg[List]<T>::FFT()').
523 #ifdef cimg_use_fftw3
524 extern "C" {
525 #include "fftw3.h"
526 }
527 #endif
528 
529 // Configure LibBoard support.
530 // (http://libboard.sourceforge.net/)
531 //
532 // Define 'cimg_use_board' to enable Board support.
533 //
534 // Board library may be used to draw 3d objects in vector-graphics canvas
535 // that can be saved as '.ps' or '.svg' files afterwards.
536 // (see method 'CImg<T>::draw_object3d()').
537 #ifdef cimg_use_board
538 #include "Board.h"
539 #endif
540 
541 // Configure OpenEXR support.
542 // (http://www.openexr.com/)
543 //
544 // Define 'cimg_use_openexr' to enable OpenEXR support.
545 //
546 // OpenEXR library may be used to get a native support of '.exr' files.
547 // (see methods 'CImg<T>::{load,save}_exr()').
548 #ifdef cimg_use_openexr
549 #include "ImfRgbaFile.h"
550 #include "ImfInputFile.h"
551 #include "ImfChannelList.h"
552 #include "ImfMatrixAttribute.h"
553 #include "ImfArray.h"
554 #endif
555 
556 // Configure TinyEXR support.
557 // (https://github.com/syoyo/tinyexr)
558 //
559 // Define 'cimg_use_tinyexr' to enable TinyEXR support.
560 //
561 // TinyEXR is a small, single header-only library to load and save OpenEXR(.exr) images.
562 #ifdef cimg_use_tinyexr
563 #ifndef TINYEXR_IMPLEMENTATION
564 #define TINYEXR_IMPLEMENTATION
565 #endif
566 #include "tinyexr.h"
567 #endif
568 
569 // Lapack configuration.
570 // (http://www.netlib.org/lapack)
571 //
572 // Define 'cimg_use_lapack' to enable LAPACK support.
573 //
574 // Lapack library may be used in several CImg methods to speed up
575 // matrix computations (eigenvalues, inverse, ...).
576 #ifdef cimg_use_lapack
577 extern "C" {
578   extern void sgetrf_(int*, int*, float*, int*, int*, int*);
579   extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
580   extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
581   extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
582   extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
583   extern void dgetrf_(int*, int*, double*, int*, int*, int*);
584   extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
585   extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
586   extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*,
587                       int*, double*, int*, double*, int*, int*);
588   extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
589   extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*);
590   extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*);
591 }
592 #endif
593 
594 // Check if min/max/PI macros are defined.
595 //
596 // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
597 // because it redefines functions min(), max() and const variable PI in the cimg:: namespace.
598 // so it '#undef' these macros if necessary, and restore them to reasonable
599 // values at the end of this file.
600 #ifdef min
601 #undef min
602 #define _cimg_redefine_min
603 #endif
604 #ifdef max
605 #undef max
606 #define _cimg_redefine_max
607 #endif
608 #ifdef PI
609 #undef PI
610 #define _cimg_redefine_PI
611 #endif
612 
613 // Define 'cimg_library' namespace suffix.
614 //
615 // You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work
616 // with several versions of the library at the same time.
617 #ifdef cimg_namespace_suffix
618 #define __cimg_library_suffixed(s) cimg_library_##s
619 #define _cimg_library_suffixed(s) __cimg_library_suffixed(s)
620 #define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix)
621 #else
622 #define cimg_library_suffixed cimg_library
623 #endif
624 
625 /*------------------------------------------------------------------------------
626   #
627   # Define user-friendly macros.
628   #
629   # These CImg macros are prefixed by 'cimg_' and can be used safely in your own
630   # code. They are useful to parse command line options, or to write image loops.
631   #
632   ------------------------------------------------------------------------------*/
633 
634 // Macros to define program usage, and retrieve command line arguments.
635 #define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
636 #define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0)
637 #define cimg_option(name,defaut,usage) cimg_library_suffixed::cimg::option(name,argc,argv,defaut,usage)
638 
639 // Macros to define and manipulate local neighborhoods.
640 #define CImg_2x2(I,T) T I[4]; \
641                       T& I##cc = I[0]; T& I##nc = I[1]; \
642                       T& I##cn = I[2]; T& I##nn = I[3]; \
643                       I##cc = I##nc = \
644                       I##cn = I##nn = 0
645 
646 #define CImg_3x3(I,T) T I[9]; \
647                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
648                       T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
649                       T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
650                       I##pp = I##cp = I##np = \
651                       I##pc = I##cc = I##nc = \
652                       I##pn = I##cn = I##nn = 0
653 
654 #define CImg_4x4(I,T) T I[16]; \
655                       T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
656                       T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
657                       T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
658                       T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
659                       I##pp = I##cp = I##np = I##ap = \
660                       I##pc = I##cc = I##nc = I##ac = \
661                       I##pn = I##cn = I##nn = I##an = \
662                       I##pa = I##ca = I##na = I##aa = 0
663 
664 #define CImg_5x5(I,T) T I[25]; \
665                       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]; \
666                       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]; \
667                       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]; \
668                       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]; \
669                       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]; \
670                       I##bb = I##pb = I##cb = I##nb = I##ab = \
671                       I##bp = I##pp = I##cp = I##np = I##ap = \
672                       I##bc = I##pc = I##cc = I##nc = I##ac = \
673                       I##bn = I##pn = I##cn = I##nn = I##an = \
674                       I##ba = I##pa = I##ca = I##na = I##aa = 0
675 
676 #define CImg_2x2x2(I,T) T I[8]; \
677                       T& I##ccc = I[0]; T& I##ncc = I[1]; \
678                       T& I##cnc = I[2]; T& I##nnc = I[3]; \
679                       T& I##ccn = I[4]; T& I##ncn = I[5]; \
680                       T& I##cnn = I[6]; T& I##nnn = I[7]; \
681                       I##ccc = I##ncc = \
682                       I##cnc = I##nnc = \
683                       I##ccn = I##ncn = \
684                       I##cnn = I##nnn = 0
685 
686 #define CImg_3x3x3(I,T) T I[27]; \
687                       T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
688                       T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
689                       T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
690                       T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
691                       T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
692                       T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
693                       T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
694                       T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
695                       T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
696                       I##ppp = I##cpp = I##npp = \
697                       I##pcp = I##ccp = I##ncp = \
698                       I##pnp = I##cnp = I##nnp = \
699                       I##ppc = I##cpc = I##npc = \
700                       I##pcc = I##ccc = I##ncc = \
701                       I##pnc = I##cnc = I##nnc = \
702                       I##ppn = I##cpn = I##npn = \
703                       I##pcn = I##ccn = I##ncn = \
704                       I##pnn = I##cnn = I##nnn = 0
705 
706 #define cimg_get2x2(img,x,y,z,c,I,T) \
707   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), \
708   I[3] = (T)(img)(_n1##x,_n1##y,z,c)
709 
710 #define cimg_get3x3(img,x,y,z,c,I,T) \
711   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), \
712   I[3] = (T)(img)(_p1##x,y,z,c), I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), \
713   I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), I[8] = (T)(img)(_n1##x,_n1##y,z,c)
714 
715 #define cimg_get4x4(img,x,y,z,c,I,T) \
716   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), \
717   I[3] = (T)(img)(_n2##x,_p1##y,z,c), I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), \
718   I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), I[8] = (T)(img)(_p1##x,_n1##y,z,c), \
719   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), \
720   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), \
721   I[15] = (T)(img)(_n2##x,_n2##y,z,c)
722 
723 #define cimg_get5x5(img,x,y,z,c,I,T) \
724   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), \
725   I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), \
726   I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), I[8] = (T)(img)(_n1##x,_p1##y,z,c), \
727   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), \
728   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), \
729   I[15] = (T)(img)(_p2##x,_n1##y,z,c), I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), \
730   I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), I[20] = (T)(img)(_p2##x,_n2##y,z,c), \
731   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), \
732   I[24] = (T)(img)(_n2##x,_n2##y,z,c)
733 
734 #define cimg_get6x6(img,x,y,z,c,I,T) \
735   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), \
736   I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), \
737   I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), I[8] = (T)(img)(x,_p1##y,z,c), \
738   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), \
739   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), \
740   I[15] = (T)(img)(_n1##x,y,z,c), I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), \
741   I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), I[20] = (T)(img)(x,_n1##y,z,c), \
742   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), \
743   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), \
744   I[27] = (T)(img)(_n1##x,_n2##y,z,c), I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), \
745   I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), I[32] = (T)(img)(x,_n3##y,z,c), \
746   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)
747 
748 #define cimg_get7x7(img,x,y,z,c,I,T) \
749   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), \
750   I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \
751   I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), I[8] = (T)(img)(_p2##x,_p2##y,z,c), \
752   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), \
753   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), \
754   I[15] = (T)(img)(_p2##x,_p1##y,z,c), I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), \
755   I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), I[20] = (T)(img)(_n3##x,_p1##y,z,c), \
756   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), \
757   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), \
758   I[27] = (T)(img)(_n3##x,y,z,c), I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), \
759   I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), I[32] = (T)(img)(_n1##x,_n1##y,z,c), \
760   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), \
761   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), \
762   I[39] = (T)(img)(_n1##x,_n2##y,z,c), I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), \
763   I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), I[44] = (T)(img)(_p1##x,_n3##y,z,c), \
764   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), \
765   I[48] = (T)(img)(_n3##x,_n3##y,z,c)
766 
767 #define cimg_get8x8(img,x,y,z,c,I,T) \
768   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), \
769   I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \
770   I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), I[8] = (T)(img)(_p3##x,_p2##y,z,c), \
771   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), \
772   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), \
773   I[15] = (T)(img)(_n4##x,_p2##y,z,c), I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), \
774   I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), I[20] = (T)(img)(_n1##x,_p1##y,z,c), \
775   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), \
776   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), \
777   I[27] = (T)(img)(x,y,z,c), I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), \
778   I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), I[32] = (T)(img)(_p3##x,_n1##y,z,c), \
779   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), \
780   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), \
781   I[39] = (T)(img)(_n4##x,_n1##y,z,c), I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), \
782   I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), I[44] = (T)(img)(_n1##x,_n2##y,z,c), \
783   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), \
784   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), \
785   I[51] = (T)(img)(x,_n3##y,z,c), I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), \
786   I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), I[56] = (T)(img)(_p3##x,_n4##y,z,c), \
787   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), \
788   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), \
789   I[63] = (T)(img)(_n4##x,_n4##y,z,c);
790 
791 #define cimg_get9x9(img,x,y,z,c,I,T) \
792   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), \
793   I[3] = (T)(img)(_p1##x,_p4##y,z,c), I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), \
794   I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), I[8] = (T)(img)(_n4##x,_p4##y,z,c), \
795   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), \
796   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), \
797   I[15] = (T)(img)(_n2##x,_p3##y,z,c), I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), \
798   I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), I[20] = (T)(img)(_p2##x,_p2##y,z,c), \
799   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), \
800   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), \
801   I[27] = (T)(img)(_p4##x,_p1##y,z,c), I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), \
802   I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), I[32] = (T)(img)(_n1##x,_p1##y,z,c), \
803   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), \
804   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), \
805   I[39] = (T)(img)(_p1##x,y,z,c), I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), \
806   I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), I[44] = (T)(img)(_n4##x,y,z,c), \
807   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), \
808   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), \
809   I[51] = (T)(img)(_n2##x,_n1##y,z,c), I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), \
810   I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), I[56] = (T)(img)(_p2##x,_n2##y,z,c), \
811   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), \
812   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), \
813   I[63] = (T)(img)(_p4##x,_n3##y,z,c), I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), \
814   I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), I[68] = (T)(img)(_n1##x,_n3##y,z,c), \
815   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), \
816   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), \
817   I[75] = (T)(img)(_p1##x,_n4##y,z,c), I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), \
818   I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), I[80] = (T)(img)(_n4##x,_n4##y,z,c)
819 
820 #define cimg_get2x2x2(img,x,y,z,c,I,T) \
821   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), \
822   I[3] = (T)(img)(_n1##x,_n1##y,z,c), I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), \
823   I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
824 
825 #define cimg_get3x3x3(img,x,y,z,c,I,T) \
826   I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \
827   I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), \
828   I[5] = (T)(img)(_n1##x,y,_p1##z,c), I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), \
829   I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), \
830   I[11] = (T)(img)(_n1##x,_p1##y,z,c), I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), \
831   I[14] = (T)(img)(_n1##x,y,z,c), I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), \
832   I[17] = (T)(img)(_n1##x,_n1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), \
833   I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), \
834   I[23] = (T)(img)(_n1##x,y,_n1##z,c), I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), \
835   I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
836 
837 // Macros to perform various image loops.
838 //
839 // These macros are simpler to use than loops with C++ iterators.
840 #define cimg_for(img,ptrs,T_ptrs) \
841   for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs)
842 #define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs)
843 #define cimg_foroff(img,off) for (cimg_ulong off = 0, _max##off = (img).size(); off<_max##off; ++off)
844 
845 #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
846 #define cimg_forX(img,x) cimg_for1((img)._width,x)
847 #define cimg_forY(img,y) cimg_for1((img)._height,y)
848 #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
849 #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
850 #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
851 #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
852 #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
853 #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
854 #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
855 #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
856 #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
857 #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
858 #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
859 #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
860 #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
861 
862 #define cimg_rof1(bound,i) for (int i = (int)(bound) - 1; i>=0; --i)
863 #define cimg_rofX(img,x) cimg_rof1((img)._width,x)
864 #define cimg_rofY(img,y) cimg_rof1((img)._height,y)
865 #define cimg_rofZ(img,z) cimg_rof1((img)._depth,z)
866 #define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c)
867 #define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x)
868 #define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x)
869 #define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y)
870 #define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x)
871 #define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y)
872 #define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z)
873 #define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y)
874 #define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y)
875 #define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z)
876 #define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z)
877 #define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z)
878 
879 #define cimg_for_in1(bound,i0,i1,i) \
880  for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound) - 1; i<=_max##i; ++i)
881 #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
882 #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
883 #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
884 #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
885 #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)
886 #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)
887 #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)
888 #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)
889 #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)
890 #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)
891 #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)
892 #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)
893 #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)
894 #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)
895 #define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
896   cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
897 #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width - 1 - (n),x)
898 #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height - 1 - (n),y)
899 #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth  - 1 - (n),z)
900 #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum - 1 - (n),c)
901 #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
902 #define cimg_for_insideXYZ(img,x,y,z,n) \
903   cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
904 #define cimg_for_insideXYZC(img,x,y,z,c,n) \
905   cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
906 
907 #define cimg_for_out1(boundi,i0,i1,i) \
908  for (int i = (int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1) + 1:i)
909 #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
910  for (int j = 0; j<(int)(boundj); ++j) \
911  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
912   ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1) + 1:i))
913 #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
914  for (int k = 0; k<(int)(boundk); ++k) \
915  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
916  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
917   ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1) + 1:i))
918 #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
919  for (int l = 0; l<(int)(boundl); ++l) \
920  for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
921  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
922  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1) + 1; \
923   i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1) + 1:i))
924 #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
925 #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
926 #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
927 #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
928 #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
929 #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
930 #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
931 #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
932 #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
933 #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
934 #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \
935   cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z)
936 #define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \
937   cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c)
938 #define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \
939   cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c)
940 #define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \
941   cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c)
942 #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
943  cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
944 #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width - 1 - (n),x)
945 #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height - 1 - (n),y)
946 #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth - 1 - (n),z)
947 #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum - 1 - (n),c)
948 #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
949 #define cimg_for_borderXYZ(img,x,y,z,n) \
950   cimg_for_outXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
951 #define cimg_for_borderXYZC(img,x,y,z,c,n) \
952  cimg_for_outXYZC(img,n,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n), \
953                   (img)._depth - 1 - (n),(img)._spectrum - 1 - (n),x,y,z,c)
954 
955 #define cimg_for_spiralXY(img,x,y) \
956  for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
957       --_n1##y, _n1##x+=(_n1##x>>2) - ((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width - 1 - ++x:\
958       ((_n1##x&3)==2?(img)._height - 1 - ++y:--x))))?0:1)
959 
960 #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
961  for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
962       _dx=(x1)>(x0)?(int)(x1) - (int)(x0):(_sx=-1,(int)(x0) - (int)(x1)), \
963       _dy=(y1)>(y0)?(int)(y1) - (int)(y0):(_sy=-1,(int)(y0) - (int)(y1)), \
964       _counter = _dx, \
965       _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
966       _counter>=0; \
967       --_counter, x+=_steep? \
968       (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
969       (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
970 
971 #define cimg_for2(bound,i) \
972  for (int i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
973       _n1##i<(int)(bound) || i==--_n1##i; \
974       ++i, ++_n1##i)
975 #define cimg_for2X(img,x) cimg_for2((img)._width,x)
976 #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
977 #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
978 #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
979 #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
980 #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
981 #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
982 #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
983 #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
984 #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
985 #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
986 #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
987 #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
988 #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
989 
990 #define cimg_for_in2(bound,i0,i1,i) \
991  for (int i = (int)(i0)<0?0:(int)(i0), \
992       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
993       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
994       ++i, ++_n1##i)
995 #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
996 #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
997 #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
998 #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
999 #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)
1000 #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)
1001 #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)
1002 #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)
1003 #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)
1004 #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)
1005 #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)
1006 #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)
1007 #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)
1008 #define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1009   cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1010 
1011 #define cimg_for3(bound,i) \
1012  for (int i = 0, _p1##i = 0, \
1013       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
1014       _n1##i<(int)(bound) || i==--_n1##i; \
1015       _p1##i = i++, ++_n1##i)
1016 #define cimg_for3X(img,x) cimg_for3((img)._width,x)
1017 #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
1018 #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
1019 #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
1020 #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
1021 #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
1022 #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
1023 #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
1024 #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
1025 #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
1026 #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
1027 #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
1028 #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
1029 #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
1030 
1031 #define cimg_for_in3(bound,i0,i1,i) \
1032  for (int i = (int)(i0)<0?0:(int)(i0), \
1033       _p1##i = i - 1<0?0:i - 1, \
1034       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
1035       i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
1036       _p1##i = i++, ++_n1##i)
1037 #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
1038 #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
1039 #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
1040 #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
1041 #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)
1042 #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)
1043 #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)
1044 #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)
1045 #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)
1046 #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)
1047 #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)
1048 #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)
1049 #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)
1050 #define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1051   cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1052 
1053 #define cimg_for4(bound,i) \
1054  for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1055       _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
1056       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
1057       _p1##i = i++, ++_n1##i, ++_n2##i)
1058 #define cimg_for4X(img,x) cimg_for4((img)._width,x)
1059 #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
1060 #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
1061 #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
1062 #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
1063 #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
1064 #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
1065 #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
1066 #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
1067 #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
1068 #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
1069 #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
1070 #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
1071 #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
1072 
1073 #define cimg_for_in4(bound,i0,i1,i) \
1074  for (int i = (int)(i0)<0?0:(int)(i0), \
1075       _p1##i = i - 1<0?0:i - 1, \
1076       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1077       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
1078       i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
1079       _p1##i = i++, ++_n1##i, ++_n2##i)
1080 #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
1081 #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
1082 #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
1083 #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
1084 #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)
1085 #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)
1086 #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)
1087 #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)
1088 #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)
1089 #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)
1090 #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)
1091 #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)
1092 #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)
1093 #define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1094   cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1095 
1096 #define cimg_for5(bound,i) \
1097  for (int i = 0, _p2##i = 0, _p1##i = 0, \
1098       _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1099       _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
1100       _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
1101       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
1102 #define cimg_for5X(img,x) cimg_for5((img)._width,x)
1103 #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
1104 #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
1105 #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
1106 #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
1107 #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
1108 #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
1109 #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
1110 #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
1111 #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
1112 #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
1113 #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
1114 #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
1115 #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
1116 
1117 #define cimg_for_in5(bound,i0,i1,i) \
1118  for (int i = (int)(i0)<0?0:(int)(i0), \
1119       _p2##i = i - 2<0?0:i - 2, \
1120       _p1##i = i - 1<0?0:i - 1, \
1121       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1122       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
1123       i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
1124       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
1125 #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
1126 #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
1127 #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
1128 #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
1129 #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)
1130 #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)
1131 #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)
1132 #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)
1133 #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)
1134 #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)
1135 #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)
1136 #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)
1137 #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)
1138 #define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1139   cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1140 
1141 #define cimg_for6(bound,i) \
1142  for (int i = 0, _p2##i = 0, _p1##i = 0, \
1143       _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1144       _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
1145       _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
1146       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
1147       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1148 #define cimg_for6X(img,x) cimg_for6((img)._width,x)
1149 #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
1150 #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
1151 #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
1152 #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
1153 #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
1154 #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
1155 #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
1156 #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
1157 #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
1158 #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
1159 #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
1160 #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
1161 #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
1162 
1163 #define cimg_for_in6(bound,i0,i1,i) \
1164  for (int i = (int)(i0)<0?0:(int)(i0), \
1165       _p2##i = i - 2<0?0:i - 2, \
1166       _p1##i = i - 1<0?0:i - 1, \
1167       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1168       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
1169       _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
1170       i<=(int)(i1) && \
1171       (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
1172       _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1173 #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
1174 #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
1175 #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
1176 #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
1177 #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)
1178 #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)
1179 #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)
1180 #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)
1181 #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)
1182 #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)
1183 #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)
1184 #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)
1185 #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)
1186 #define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1187   cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1188 
1189 #define cimg_for7(bound,i) \
1190  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1191       _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1192       _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
1193       _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
1194       _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
1195       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1196 #define cimg_for7X(img,x) cimg_for7((img)._width,x)
1197 #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
1198 #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
1199 #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
1200 #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
1201 #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
1202 #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
1203 #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
1204 #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
1205 #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
1206 #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
1207 #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
1208 #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
1209 #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
1210 
1211 #define cimg_for_in7(bound,i0,i1,i) \
1212  for (int i = (int)(i0)<0?0:(int)(i0), \
1213       _p3##i = i - 3<0?0:i - 3, \
1214       _p2##i = i - 2<0?0:i - 2, \
1215       _p1##i = i - 1<0?0:i - 1, \
1216       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1217       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
1218       _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
1219       i<=(int)(i1) && \
1220       (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
1221       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
1222 #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
1223 #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
1224 #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
1225 #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
1226 #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)
1227 #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)
1228 #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)
1229 #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)
1230 #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)
1231 #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)
1232 #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)
1233 #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)
1234 #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)
1235 #define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1236   cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1237 
1238 #define cimg_for8(bound,i) \
1239  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1240       _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
1241       _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
1242       _n3##i = 3>=(bound)?(int)(bound) - 1:3, \
1243       _n4##i = 4>=(bound)?(int)(bound) - 1:4; \
1244       _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1245       i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1246       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1247 #define cimg_for8X(img,x) cimg_for8((img)._width,x)
1248 #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
1249 #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
1250 #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
1251 #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
1252 #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
1253 #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
1254 #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
1255 #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
1256 #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
1257 #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
1258 #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
1259 #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
1260 #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
1261 
1262 #define cimg_for_in8(bound,i0,i1,i) \
1263  for (int i = (int)(i0)<0?0:(int)(i0), \
1264       _p3##i = i - 3<0?0:i - 3, \
1265       _p2##i = i - 2<0?0:i - 2, \
1266       _p1##i = i - 1<0?0:i - 1, \
1267       _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1268       _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
1269       _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
1270       _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
1271       i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1272       i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1273       _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1274 #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
1275 #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
1276 #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
1277 #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
1278 #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)
1279 #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)
1280 #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)
1281 #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)
1282 #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)
1283 #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)
1284 #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)
1285 #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)
1286 #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)
1287 #define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1288   cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1289 
1290 #define cimg_for9(bound,i) \
1291   for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1292        _n1##i = 1>=(int)(bound)?(int)(bound) - 1:1, \
1293        _n2##i = 2>=(int)(bound)?(int)(bound) - 1:2, \
1294        _n3##i = 3>=(int)(bound)?(int)(bound) - 1:3, \
1295        _n4##i = 4>=(int)(bound)?(int)(bound) - 1:4; \
1296        _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1297        i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1298        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1299 #define cimg_for9X(img,x) cimg_for9((img)._width,x)
1300 #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
1301 #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
1302 #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
1303 #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
1304 #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
1305 #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
1306 #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
1307 #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
1308 #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
1309 #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
1310 #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
1311 #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
1312 #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
1313 
1314 #define cimg_for_in9(bound,i0,i1,i) \
1315   for (int i = (int)(i0)<0?0:(int)(i0), \
1316        _p4##i = i - 4<0?0:i - 4, \
1317        _p3##i = i - 3<0?0:i - 3, \
1318        _p2##i = i - 2<0?0:i - 2, \
1319        _p1##i = i - 1<0?0:i - 1, \
1320        _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
1321        _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
1322        _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
1323        _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
1324        i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1325        i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1326        _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1327 #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
1328 #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
1329 #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
1330 #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
1331 #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)
1332 #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)
1333 #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)
1334 #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)
1335 #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)
1336 #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)
1337 #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)
1338 #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)
1339 #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)
1340 #define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
1341   cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1342 
1343 #define cimg_for2x2(img,x,y,z,c,I,T) \
1344   cimg_for2((img)._height,y) for (int x = 0, \
1345    _n1##x = (int)( \
1346    (I[0] = (T)(img)(0,y,z,c)), \
1347    (I[2] = (T)(img)(0,_n1##y,z,c)), \
1348    1>=(img)._width?(img).width() - 1:1);  \
1349    (_n1##x<(img).width() && ( \
1350    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1351    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1352    x==--_n1##x; \
1353    I[0] = I[1], \
1354    I[2] = I[3], \
1355    ++x, ++_n1##x)
1356 
1357 #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1358   cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1359    _n1##x = (int)( \
1360    (I[0] = (T)(img)(x,y,z,c)), \
1361    (I[2] = (T)(img)(x,_n1##y,z,c)), \
1362    x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
1363    x<=(int)(x1) && ((_n1##x<(img).width() && (  \
1364    (I[1] = (T)(img)(_n1##x,y,z,c)), \
1365    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1366    x==--_n1##x); \
1367    I[0] = I[1], \
1368    I[2] = I[3], \
1369    ++x, ++_n1##x)
1370 
1371 #define cimg_for3x3(img,x,y,z,c,I,T) \
1372   cimg_for3((img)._height,y) for (int x = 0, \
1373    _p1##x = 0, \
1374    _n1##x = (int)( \
1375    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1376    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
1377    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
1378    1>=(img)._width?(img).width() - 1:1); \
1379    (_n1##x<(img).width() && ( \
1380    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1381    (I[5] = (T)(img)(_n1##x,y,z,c)), \
1382    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1383    x==--_n1##x; \
1384    I[0] = I[1], I[1] = I[2], \
1385    I[3] = I[4], I[4] = I[5], \
1386    I[6] = I[7], I[7] = I[8], \
1387    _p1##x = x++, ++_n1##x)
1388 
1389 #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1390   cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1391    _p1##x = x - 1<0?0:x - 1, \
1392    _n1##x = (int)( \
1393    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1394    (I[3] = (T)(img)(_p1##x,y,z,c)), \
1395    (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
1396    (I[1] = (T)(img)(x,_p1##y,z,c)), \
1397    (I[4] = (T)(img)(x,y,z,c)), \
1398    (I[7] = (T)(img)(x,_n1##y,z,c)), \
1399    x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
1400    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1401    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1402    (I[5] = (T)(img)(_n1##x,y,z,c)), \
1403    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1404    x==--_n1##x);	    \
1405    I[0] = I[1], I[1] = I[2], \
1406    I[3] = I[4], I[4] = I[5], \
1407    I[6] = I[7], I[7] = I[8], \
1408    _p1##x = x++, ++_n1##x)
1409 
1410 #define cimg_for4x4(img,x,y,z,c,I,T) \
1411   cimg_for4((img)._height,y) for (int x = 0, \
1412    _p1##x = 0, \
1413    _n1##x = 1>=(img)._width?(img).width() - 1:1, \
1414    _n2##x = (int)( \
1415    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1416    (I[4] = I[5] = (T)(img)(0,y,z,c)), \
1417    (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
1418    (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
1419    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1420    (I[6] = (T)(img)(_n1##x,y,z,c)), \
1421    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1422    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1423    2>=(img)._width?(img).width() - 1:2); \
1424    (_n2##x<(img).width() && ( \
1425    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1426    (I[7] = (T)(img)(_n2##x,y,z,c)), \
1427    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1428    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1429    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1430    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1431    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1432    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1433    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1434    _p1##x = x++, ++_n1##x, ++_n2##x)
1435 
1436 #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1437   cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1438    _p1##x = x - 1<0?0:x - 1, \
1439    _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
1440    _n2##x = (int)( \
1441    (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1442    (I[4] = (T)(img)(_p1##x,y,z,c)), \
1443    (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
1444    (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
1445    (I[1] = (T)(img)(x,_p1##y,z,c)), \
1446    (I[5] = (T)(img)(x,y,z,c)), \
1447    (I[9] = (T)(img)(x,_n1##y,z,c)), \
1448    (I[13] = (T)(img)(x,_n2##y,z,c)), \
1449    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1450    (I[6] = (T)(img)(_n1##x,y,z,c)), \
1451    (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1452    (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1453    x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
1454    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1455    (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1456    (I[7] = (T)(img)(_n2##x,y,z,c)), \
1457    (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1458    (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1459    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1460    I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1461    I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1462    I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1463    I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1464    _p1##x = x++, ++_n1##x, ++_n2##x)
1465 
1466 #define cimg_for5x5(img,x,y,z,c,I,T) \
1467  cimg_for5((img)._height,y) for (int x = 0, \
1468    _p2##x = 0, _p1##x = 0, \
1469    _n1##x = 1>=(img)._width?(img).width() - 1:1, \
1470    _n2##x = (int)( \
1471    (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1472    (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
1473    (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
1474    (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
1475    (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
1476    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1477    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1478    (I[13] = (T)(img)(_n1##x,y,z,c)), \
1479    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1480    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)),  \
1481    2>=(img)._width?(img).width() - 1:2); \
1482    (_n2##x<(img).width() && ( \
1483    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1484    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1485    (I[14] = (T)(img)(_n2##x,y,z,c)), \
1486    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1487    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1488    _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1489    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1490    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1491    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1492    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1493    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1494    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1495 
1496 #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1497  cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1498    _p2##x = x - 2<0?0:x - 2, \
1499    _p1##x = x - 1<0?0:x - 1, \
1500    _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
1501    _n2##x = (int)( \
1502    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1503    (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
1504    (I[10] = (T)(img)(_p2##x,y,z,c)), \
1505    (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
1506    (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
1507    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1508    (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
1509    (I[11] = (T)(img)(_p1##x,y,z,c)), \
1510    (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
1511    (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
1512    (I[2] = (T)(img)(x,_p2##y,z,c)), \
1513    (I[7] = (T)(img)(x,_p1##y,z,c)), \
1514    (I[12] = (T)(img)(x,y,z,c)), \
1515    (I[17] = (T)(img)(x,_n1##y,z,c)), \
1516    (I[22] = (T)(img)(x,_n2##y,z,c)), \
1517    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1518    (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1519    (I[13] = (T)(img)(_n1##x,y,z,c)), \
1520    (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1521    (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
1522    x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
1523    x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1524    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1525    (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1526    (I[14] = (T)(img)(_n2##x,y,z,c)), \
1527    (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1528    (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1529    _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1530    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1531    I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1532    I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1533    I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1534    I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1535    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1536 
1537 #define cimg_for6x6(img,x,y,z,c,I,T) \
1538  cimg_for6((img)._height,y) for (int x = 0, \
1539    _p2##x = 0, _p1##x = 0, \
1540    _n1##x = 1>=(img)._width?(img).width() - 1:1, \
1541    _n2##x = 2>=(img)._width?(img).width() - 1:2, \
1542    _n3##x = (int)( \
1543    (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1544    (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
1545    (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
1546    (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
1547    (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
1548    (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
1549    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1550    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1551    (I[15] = (T)(img)(_n1##x,y,z,c)), \
1552    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1553    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1554    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1555    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1556    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1557    (I[16] = (T)(img)(_n2##x,y,z,c)), \
1558    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1559    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1560    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1561    3>=(img)._width?(img).width() - 1:3); \
1562    (_n3##x<(img).width() && ( \
1563    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1564    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1565    (I[17] = (T)(img)(_n3##x,y,z,c)), \
1566    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1567    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1568    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1569    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
1570    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1571    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1572    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1573    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1574    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1575    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1576    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1577 
1578 #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1579   cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
1580    _p2##x = x - 2<0?0:x - 2, \
1581    _p1##x = x - 1<0?0:x - 1, \
1582    _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
1583    _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
1584    _n3##x = (int)( \
1585    (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1586    (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
1587    (I[12] = (T)(img)(_p2##x,y,z,c)), \
1588    (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
1589    (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
1590    (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
1591    (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1592    (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
1593    (I[13] = (T)(img)(_p1##x,y,z,c)), \
1594    (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
1595    (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
1596    (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
1597    (I[2] = (T)(img)(x,_p2##y,z,c)), \
1598    (I[8] = (T)(img)(x,_p1##y,z,c)), \
1599    (I[14] = (T)(img)(x,y,z,c)), \
1600    (I[20] = (T)(img)(x,_n1##y,z,c)), \
1601    (I[26] = (T)(img)(x,_n2##y,z,c)), \
1602    (I[32] = (T)(img)(x,_n3##y,z,c)), \
1603    (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1604    (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1605    (I[15] = (T)(img)(_n1##x,y,z,c)), \
1606    (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1607    (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1608    (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1609    (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1610    (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1611    (I[16] = (T)(img)(_n2##x,y,z,c)), \
1612    (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1613    (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1614    (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1615    x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
1616    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1617    (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1618    (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1619    (I[17] = (T)(img)(_n3##x,y,z,c)), \
1620    (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1621    (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1622    (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1623    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
1624    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1625    I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1626    I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1627    I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1628    I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1629    I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1630    _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1631 
1632 #define cimg_for7x7(img,x,y,z,c,I,T) \
1633   cimg_for7((img)._height,y) for (int x = 0, \
1634    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1635    _n1##x = 1>=(img)._width?(img).width() - 1:1, \
1636    _n2##x = 2>=(img)._width?(img).width() - 1:2, \
1637    _n3##x = (int)( \
1638    (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1639    (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
1640    (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
1641    (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
1642    (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
1643    (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
1644    (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
1645    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1646    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1647    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1648    (I[25] = (T)(img)(_n1##x,y,z,c)), \
1649    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1650    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1651    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1652    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1653    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1654    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1655    (I[26] = (T)(img)(_n2##x,y,z,c)), \
1656    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1657    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1658    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1659    3>=(img)._width?(img).width() - 1:3); \
1660    (_n3##x<(img).width() && ( \
1661    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1662    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1663    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1664    (I[27] = (T)(img)(_n3##x,y,z,c)), \
1665    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1666    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1667    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1668    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
1669    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1670    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1671    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1672    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1673    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1674    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1675    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1676    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1677 
1678 #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1679   cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1680    _p3##x = x - 3<0?0:x - 3, \
1681    _p2##x = x - 2<0?0:x - 2, \
1682    _p1##x = x - 1<0?0:x - 1, \
1683    _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
1684    _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
1685    _n3##x = (int)( \
1686    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1687    (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
1688    (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
1689    (I[21] = (T)(img)(_p3##x,y,z,c)), \
1690    (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
1691    (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
1692    (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
1693    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1694    (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
1695    (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
1696    (I[22] = (T)(img)(_p2##x,y,z,c)), \
1697    (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
1698    (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
1699    (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
1700    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1701    (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
1702    (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
1703    (I[23] = (T)(img)(_p1##x,y,z,c)), \
1704    (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
1705    (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
1706    (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
1707    (I[3] = (T)(img)(x,_p3##y,z,c)), \
1708    (I[10] = (T)(img)(x,_p2##y,z,c)), \
1709    (I[17] = (T)(img)(x,_p1##y,z,c)), \
1710    (I[24] = (T)(img)(x,y,z,c)), \
1711    (I[31] = (T)(img)(x,_n1##y,z,c)), \
1712    (I[38] = (T)(img)(x,_n2##y,z,c)), \
1713    (I[45] = (T)(img)(x,_n3##y,z,c)), \
1714    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1715    (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1716    (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1717    (I[25] = (T)(img)(_n1##x,y,z,c)), \
1718    (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1719    (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1720    (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1721    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1722    (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1723    (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1724    (I[26] = (T)(img)(_n2##x,y,z,c)), \
1725    (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1726    (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1727    (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1728    x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
1729    x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1730    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1731    (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1732    (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1733    (I[27] = (T)(img)(_n3##x,y,z,c)), \
1734    (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1735    (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1736    (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1737    _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
1738    I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1739    I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1740    I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1741    I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1742    I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1743    I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1744    I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1745    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1746 
1747 #define cimg_for8x8(img,x,y,z,c,I,T) \
1748   cimg_for8((img)._height,y) for (int x = 0, \
1749    _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1750    _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
1751    _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
1752    _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
1753    _n4##x = (int)( \
1754    (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1755    (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
1756    (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
1757    (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
1758    (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
1759    (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
1760    (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
1761    (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
1762    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1763    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1764    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1765    (I[28] = (T)(img)(_n1##x,y,z,c)), \
1766    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1767    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1768    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1769    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1770    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1771    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1772    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1773    (I[29] = (T)(img)(_n2##x,y,z,c)), \
1774    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1775    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1776    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1777    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1778    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1779    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1780    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1781    (I[30] = (T)(img)(_n3##x,y,z,c)), \
1782    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1783    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1784    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1785    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1786    4>=((img)._width)?(img).width() - 1:4); \
1787    (_n4##x<(img).width() && ( \
1788    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1789    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1790    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1791    (I[31] = (T)(img)(_n4##x,y,z,c)), \
1792    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1793    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1794    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1795    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1796    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1797    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], \
1798    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], \
1799    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], \
1800    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], \
1801    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], \
1802    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], \
1803    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], \
1804    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], \
1805    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1806 
1807 #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1808   cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1809    _p3##x = x - 3<0?0:x - 3, \
1810    _p2##x = x - 2<0?0:x - 2, \
1811    _p1##x = x - 1<0?0:x - 1, \
1812    _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
1813    _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
1814    _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
1815    _n4##x = (int)( \
1816    (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1817    (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
1818    (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
1819    (I[24] = (T)(img)(_p3##x,y,z,c)), \
1820    (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
1821    (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
1822    (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
1823    (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
1824    (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1825    (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
1826    (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
1827    (I[25] = (T)(img)(_p2##x,y,z,c)), \
1828    (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
1829    (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
1830    (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
1831    (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
1832    (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1833    (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
1834    (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
1835    (I[26] = (T)(img)(_p1##x,y,z,c)), \
1836    (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
1837    (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
1838    (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
1839    (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
1840    (I[3] = (T)(img)(x,_p3##y,z,c)), \
1841    (I[11] = (T)(img)(x,_p2##y,z,c)), \
1842    (I[19] = (T)(img)(x,_p1##y,z,c)), \
1843    (I[27] = (T)(img)(x,y,z,c)), \
1844    (I[35] = (T)(img)(x,_n1##y,z,c)), \
1845    (I[43] = (T)(img)(x,_n2##y,z,c)), \
1846    (I[51] = (T)(img)(x,_n3##y,z,c)), \
1847    (I[59] = (T)(img)(x,_n4##y,z,c)), \
1848    (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1849    (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1850    (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1851    (I[28] = (T)(img)(_n1##x,y,z,c)), \
1852    (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1853    (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1854    (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1855    (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1856    (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1857    (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1858    (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1859    (I[29] = (T)(img)(_n2##x,y,z,c)), \
1860    (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1861    (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1862    (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1863    (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1864    (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1865    (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1866    (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1867    (I[30] = (T)(img)(_n3##x,y,z,c)), \
1868    (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1869    (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1870    (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1871    (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1872    x + 4>=(img).width()?(img).width() - 1:x + 4); \
1873    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1874    (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1875    (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1876    (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1877    (I[31] = (T)(img)(_n4##x,y,z,c)), \
1878    (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1879    (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1880    (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1881    (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1882    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1883    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], \
1884    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], \
1885    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], \
1886    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], \
1887    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], \
1888    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], \
1889    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], \
1890    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], \
1891    _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1892 
1893 #define cimg_for9x9(img,x,y,z,c,I,T) \
1894   cimg_for9((img)._height,y) for (int x = 0, \
1895    _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1896    _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
1897    _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
1898    _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
1899    _n4##x = (int)( \
1900    (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \
1901    (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
1902    (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
1903    (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
1904    (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
1905    (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
1906    (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
1907    (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
1908    (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
1909    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1910    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1911    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
1912    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
1913    (I[41] = (T)(img)(_n1##x,y,z,c)), \
1914    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
1915    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
1916    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
1917    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
1918    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
1919    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
1920    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
1921    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
1922    (I[42] = (T)(img)(_n2##x,y,z,c)), \
1923    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
1924    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
1925    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
1926    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
1927    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
1928    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
1929    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
1930    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
1931    (I[43] = (T)(img)(_n3##x,y,z,c)), \
1932    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
1933    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
1934    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
1935    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
1936    4>=((img)._width)?(img).width() - 1:4); \
1937    (_n4##x<(img).width() && ( \
1938    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
1939    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
1940    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
1941    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
1942    (I[44] = (T)(img)(_n4##x,y,z,c)), \
1943    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
1944    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
1945    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
1946    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1947    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1948    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], \
1949    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], \
1950    I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1951    I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \
1952    I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \
1953    I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1954    I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \
1955    I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \
1956    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], \
1957    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], \
1958    I[79] = I[80], \
1959    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1960 
1961 #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1962   cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1963    _p4##x = x - 4<0?0:x - 4, \
1964    _p3##x = x - 3<0?0:x - 3, \
1965    _p2##x = x - 2<0?0:x - 2, \
1966    _p1##x = x - 1<0?0:x - 1, \
1967    _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
1968    _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
1969    _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
1970    _n4##x = (int)( \
1971    (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
1972    (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
1973    (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
1974    (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
1975    (I[36] = (T)(img)(_p4##x,y,z,c)), \
1976    (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
1977    (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
1978    (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
1979    (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
1980    (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
1981    (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
1982    (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
1983    (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
1984    (I[37] = (T)(img)(_p3##x,y,z,c)), \
1985    (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
1986    (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
1987    (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
1988    (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
1989    (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
1990    (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
1991    (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
1992    (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
1993    (I[38] = (T)(img)(_p2##x,y,z,c)), \
1994    (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
1995    (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
1996    (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
1997    (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
1998    (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
1999    (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
2000    (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
2001    (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
2002    (I[39] = (T)(img)(_p1##x,y,z,c)), \
2003    (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
2004    (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
2005    (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
2006    (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
2007    (I[4] = (T)(img)(x,_p4##y,z,c)), \
2008    (I[13] = (T)(img)(x,_p3##y,z,c)), \
2009    (I[22] = (T)(img)(x,_p2##y,z,c)), \
2010    (I[31] = (T)(img)(x,_p1##y,z,c)), \
2011    (I[40] = (T)(img)(x,y,z,c)), \
2012    (I[49] = (T)(img)(x,_n1##y,z,c)), \
2013    (I[58] = (T)(img)(x,_n2##y,z,c)), \
2014    (I[67] = (T)(img)(x,_n3##y,z,c)), \
2015    (I[76] = (T)(img)(x,_n4##y,z,c)), \
2016    (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
2017    (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
2018    (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
2019    (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
2020    (I[41] = (T)(img)(_n1##x,y,z,c)), \
2021    (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
2022    (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
2023    (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
2024    (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
2025    (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
2026    (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
2027    (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
2028    (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
2029    (I[42] = (T)(img)(_n2##x,y,z,c)), \
2030    (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
2031    (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
2032    (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
2033    (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
2034    (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
2035    (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
2036    (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
2037    (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
2038    (I[43] = (T)(img)(_n3##x,y,z,c)), \
2039    (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
2040    (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
2041    (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
2042    (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
2043    x + 4>=(img).width()?(img).width() - 1:x + 4); \
2044    x<=(int)(x1) && ((_n4##x<(img).width() && ( \
2045    (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
2046    (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
2047    (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
2048    (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
2049    (I[44] = (T)(img)(_n4##x,y,z,c)), \
2050    (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
2051    (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
2052    (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
2053    (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
2054    _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
2055    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], \
2056    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], \
2057    I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
2058    I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \
2059    I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \
2060    I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
2061    I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \
2062    I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \
2063    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], \
2064    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], \
2065    I[79] = I[80], \
2066    _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
2067 
2068 #define cimg_for2x2x2(img,x,y,z,c,I,T) \
2069  cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
2070    _n1##x = (int)( \
2071    (I[0] = (T)(img)(0,y,z,c)), \
2072    (I[2] = (T)(img)(0,_n1##y,z,c)), \
2073    (I[4] = (T)(img)(0,y,_n1##z,c)), \
2074    (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
2075    1>=(img)._width?(img).width() - 1:1); \
2076    (_n1##x<(img).width() && ( \
2077    (I[1] = (T)(img)(_n1##x,y,z,c)), \
2078    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
2079    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
2080    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
2081    x==--_n1##x; \
2082    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
2083    ++x, ++_n1##x)
2084 
2085 #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
2086  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), \
2087    _n1##x = (int)( \
2088    (I[0] = (T)(img)(x,y,z,c)), \
2089    (I[2] = (T)(img)(x,_n1##y,z,c)), \
2090    (I[4] = (T)(img)(x,y,_n1##z,c)), \
2091    (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
2092    x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
2093    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
2094    (I[1] = (T)(img)(_n1##x,y,z,c)), \
2095    (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
2096    (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
2097    (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
2098    x==--_n1##x); \
2099    I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
2100    ++x, ++_n1##x)
2101 
2102 #define cimg_for3x3x3(img,x,y,z,c,I,T) \
2103  cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
2104    _p1##x = 0, \
2105    _n1##x = (int)( \
2106    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
2107    (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)),  \
2108    (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
2109    (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
2110    (I[12] = I[13] = (T)(img)(0,y,z,c)), \
2111    (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
2112    (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
2113    (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
2114    (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
2115    1>=(img)._width?(img).width() - 1:1); \
2116    (_n1##x<(img).width() && ( \
2117    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
2118    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
2119    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
2120    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
2121    (I[14] = (T)(img)(_n1##x,y,z,c)), \
2122    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
2123    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
2124    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
2125    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
2126    x==--_n1##x; \
2127    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
2128    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
2129    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
2130    _p1##x = x++, ++_n1##x)
2131 
2132 #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
2133  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), \
2134    _p1##x = x - 1<0?0:x - 1, \
2135    _n1##x = (int)( \
2136    (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
2137    (I[3] = (T)(img)(_p1##x,y,_p1##z,c)),  \
2138    (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
2139    (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
2140    (I[12] = (T)(img)(_p1##x,y,z,c)), \
2141    (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
2142    (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
2143    (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
2144    (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
2145    (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
2146    (I[4] = (T)(img)(x,y,_p1##z,c)),  \
2147    (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
2148    (I[10] = (T)(img)(x,_p1##y,z,c)), \
2149    (I[13] = (T)(img)(x,y,z,c)), \
2150    (I[16] = (T)(img)(x,_n1##y,z,c)), \
2151    (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
2152    (I[22] = (T)(img)(x,y,_n1##z,c)), \
2153    (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
2154    x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
2155    x<=(int)(x1) && ((_n1##x<(img).width() && ( \
2156    (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
2157    (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
2158    (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
2159    (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
2160    (I[14] = (T)(img)(_n1##x,y,z,c)), \
2161    (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
2162    (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
2163    (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
2164    (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
2165    x==--_n1##x); \
2166    I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
2167    I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
2168    I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
2169    _p1##x = x++, ++_n1##x)
2170 
2171 #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
2172 #define cimglist_for_in(list,l0,l1,l) \
2173   for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width - 1; \
2174   l<=_max##l; ++l)
2175 
2176 #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
2177 
2178 // Macros used to display error messages when exceptions are thrown.
2179 // You should not use these macros is your own code.
2180 #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
2181 #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
2182 #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
2183 #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
2184 #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
2185 #define cimglist_instance _width,_allocated_width,_data,pixel_type()
2186 
2187 /*------------------------------------------------
2188  #
2189  #
2190  #  Define cimg_library:: namespace
2191  #
2192  #
2193  -------------------------------------------------*/
2194 //! Contains <i>all classes and functions</i> of the \CImg library.
2195 /**
2196    This namespace is defined to avoid functions and class names collisions
2197    that could happen with the inclusion of other C++ header files.
2198    Anyway, it should not happen often and you should reasonnably start most of your
2199    \CImg-based programs with
2200    \code
2201    #include "CImg.h"
2202    using namespace cimg_library;
2203    \endcode
2204    to simplify the declaration of \CImg Library objects afterwards.
2205 **/
2206 namespace cimg_library_suffixed {
2207 
2208   // Declare the four classes of the CImg Library.
2209   template<typename T=float> struct CImg;
2210   template<typename T=float> struct CImgList;
2211   struct CImgDisplay;
2212   struct CImgException;
2213 
2214   // Declare cimg:: namespace.
2215   // This is an uncomplete namespace definition here. It only contains some
2216   // necessary stuff to ensure a correct declaration order of the classes and functions
2217   // defined afterwards.
2218   namespace cimg {
2219 
2220     // Define ascii sequences for colored terminal output.
2221 #ifdef cimg_use_vt100
2222     static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
2223     static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 };
2224     static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 };
2225     static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
2226     static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 };
2227     static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 };
2228     static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
2229     static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 };
2230     static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 };
2231     static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
2232     static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 };
2233 #else
2234     static const char t_normal[] = { 0 };
2235     static const char *const t_black = cimg::t_normal,
2236       *const t_red = cimg::t_normal,
2237       *const t_green = cimg::t_normal,
2238       *const t_yellow = cimg::t_normal,
2239       *const t_blue = cimg::t_normal,
2240       *const t_magenta = cimg::t_normal,
2241       *const t_cyan = cimg::t_normal,
2242       *const t_white = cimg::t_normal,
2243       *const t_bold = cimg::t_normal,
2244       *const t_underscore = cimg::t_normal;
2245 #endif
2246 
2247     inline std::FILE* output(std::FILE *file=0);
2248     inline void info();
2249 
2250     //! Avoid warning messages due to unused parameters. Do nothing actually.
2251     template<typename T>
unused(const T &,...)2252     inline void unused(const T&, ...) {}
2253 
2254     // [internal] Lock/unlock a mutex for managing concurrent threads.
2255     // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }.
2256     // 'n' can be in [0,31] but mutex range [0,15] is reserved by CImg.
2257     inline int mutex(const unsigned int n, const int lock_mode=1);
2258 
_exception_mode(const unsigned int value,const bool is_set)2259     inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) {
2260       static unsigned int mode = cimg_verbosity;
2261       if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); }
2262       return mode;
2263     }
2264 
2265     // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
2266     inline FILE* _stdin(const bool throw_exception=true);
2267     inline FILE* _stdout(const bool throw_exception=true);
2268     inline FILE* _stderr(const bool throw_exception=true);
2269 
2270     // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character
2271     // at the end of the string.
2272 #if cimg_OS==2 && defined(_MSC_VER)
_snprintf(char * const s,const size_t size,const char * const format,...)2273     inline int _snprintf(char *const s, const size_t size, const char *const format, ...) {
2274       va_list ap;
2275       va_start(ap,format);
2276       const int result = _vsnprintf(s,size,format,ap);
2277       va_end(ap);
2278       return result;
2279     }
2280 
_vsnprintf(char * const s,const size_t size,const char * const format,va_list ap)2281     inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) {
2282       int result = -1;
2283       cimg::mutex(6);
2284       if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap);
2285       if (result==-1) result = _vscprintf(format,ap);
2286       cimg::mutex(6,0);
2287       return result;
2288     }
2289 
2290     // Mutex-protected version of sscanf, sprintf and snprintf.
2291     // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX.
2292 #elif defined(__MACOSX__) || defined(__APPLE__)
_sscanf(const char * const s,const char * const format,...)2293     inline int _sscanf(const char *const s, const char *const format, ...) {
2294       cimg::mutex(6);
2295       va_list args;
2296       va_start(args,format);
2297       const int result = std::vsscanf(s,format,args);
2298       va_end(args);
2299       cimg::mutex(6,0);
2300       return result;
2301     }
2302 
_sprintf(char * const s,const char * const format,...)2303     inline int _sprintf(char *const s, const char *const format, ...) {
2304       cimg::mutex(6);
2305       va_list args;
2306       va_start(args,format);
2307       const int result = std::vsprintf(s,format,args);
2308       va_end(args);
2309       cimg::mutex(6,0);
2310       return result;
2311     }
2312 
_snprintf(char * const s,const size_t n,const char * const format,...)2313     inline int _snprintf(char *const s, const size_t n, const char *const format, ...) {
2314       cimg::mutex(6);
2315       va_list args;
2316       va_start(args,format);
2317       const int result = std::vsnprintf(s,n,format,args);
2318       va_end(args);
2319       cimg::mutex(6,0);
2320       return result;
2321     }
2322 
_vsnprintf(char * const s,const size_t size,const char * format,va_list ap)2323     inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) {
2324       cimg::mutex(6);
2325       const int result = std::vsnprintf(s,size,format,ap);
2326       cimg::mutex(6,0);
2327       return result;
2328     }
2329 #endif
2330 
2331     //! Set current \CImg exception mode.
2332     /**
2333        The way error messages are handled by \CImg can be changed dynamically, using this function.
2334        \param mode Desired exception mode. Possible values are:
2335        - \c 0: Hide library messages (quiet mode).
2336        - \c 1: Print library messages on the console.
2337        - \c 2: Display library messages on a dialog window.
2338        - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!).
2339        - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!).
2340      **/
exception_mode(const unsigned int mode)2341     inline unsigned int& exception_mode(const unsigned int mode) {
2342       return _exception_mode(mode,true);
2343     }
2344 
2345     //! Return current \CImg exception mode.
2346     /**
2347        \note By default, return the value of configuration macro \c cimg_verbosity
2348     **/
exception_mode()2349     inline unsigned int& exception_mode() {
2350       return _exception_mode(0,false);
2351     }
2352 
2353     //! Set current \CImg openmp mode.
2354     /**
2355        The way openmp-based methods are handled by \CImg can be changed dynamically, using this function.
2356        \param mode Desired openmp mode. Possible values are:
2357        - \c 0: Never parallelize.
2358        - \c 1: Always parallelize.
2359        - \c 2: Adaptive parallelization mode (default behavior).
2360      **/
_openmp_mode(const unsigned int value,const bool is_set)2361     inline unsigned int& _openmp_mode(const unsigned int value, const bool is_set) {
2362       static unsigned int mode = 2;
2363       if (is_set)  { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); }
2364       return mode;
2365     }
2366 
openmp_mode(const unsigned int mode)2367     inline unsigned int& openmp_mode(const unsigned int mode) {
2368       return _openmp_mode(mode,true);
2369     }
2370 
2371     //! Return current \CImg openmp mode.
openmp_mode()2372     inline unsigned int& openmp_mode() {
2373       return _openmp_mode(0,false);
2374     }
2375 
2376 #define cimg_openmp_if(cond) if (cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond)))
2377 
2378     // Display a simple dialog box, and wait for the user's response.
2379     inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK",
2380                       const char *const button2_label=0, const char *const button3_label=0,
2381                       const char *const button4_label=0, const char *const button5_label=0,
2382                       const char *const button6_label=0, const bool centering=false);
2383 
2384     // Evaluate math expression.
2385     inline double eval(const char *const expression,
2386                        const double x=0, const double y=0, const double z=0, const double c=0);
2387 
2388   }
2389 
2390   /*---------------------------------------
2391     #
2392     # Define the CImgException structures
2393     #
2394     --------------------------------------*/
2395   //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call.
2396   /**
2397      \par Overview
2398 
2399       CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException).
2400       CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead.
2401       These classes can be:
2402 
2403       - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal.
2404         This is the only \c non-derived exception class.
2405 
2406       - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid.
2407       This is probably one of the most thrown exception by \CImg.
2408       For instance, the following example throws a \c CImgArgumentException:
2409       \code
2410       CImg<float> img(100,100,1,3); // Define a 100x100 color image with float-valued pixels.
2411       img.mirror('e');              // Try to mirror image along the (non-existing) 'e'-axis.
2412       \endcode
2413 
2414       - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances.
2415 
2416       - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit
2417       the function requirements. For instance, the following example throws a \c CImgInstanceException:
2418       \code
2419       const CImg<float> img;           // Define an empty image.
2420       const float value = img.at(0);   // Try to read first pixel value (does not exist).
2421       \endcode
2422 
2423       - \b CImgIOException: Thrown when an error occured when trying to load or save image files.
2424       This happens when trying to read files that do not exist or with invalid formats.
2425       For instance, the following example throws a \c CImgIOException:
2426       \code
2427       const CImg<float> img("missing_file.jpg");  // Try to load a file that does not exist.
2428       \endcode
2429 
2430       - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and
2431       when a \CImg function has to display a warning message (see cimg::warn()).
2432 
2433       It is not recommended to throw CImgException instances by yourself,
2434       since they are expected to be thrown only by \CImg.
2435       When an error occurs in a library function call, \CImg may display error messages on the screen or on the
2436       standard output, depending on the current \CImg exception mode.
2437       The \CImg exception mode can be get and set by functions cimg::exception_mode() and
2438       cimg::exception_mode(unsigned int).
2439 
2440       \par Exceptions handling
2441 
2442       In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown.
2443       This may lead the program to break (this is the default behavior), but you can bypass this behavior by
2444       handling the exceptions by yourself,
2445       using a usual <tt>try { ... } catch () { ... }</tt> bloc, as in the following example:
2446       \code
2447       #define "CImg.h"
2448       using namespace cimg_library;
2449       int main() {
2450         cimg::exception_mode(0);                                    // Enable quiet exception mode.
2451         try {
2452           ...                                                       // Here, do what you want to stress CImg.
2453         } catch (CImgException& e) {                                // You succeeded: something went wrong!
2454           std::fprintf(stderr,"CImg Library Error: %s",e.what());   // Display your custom error message.
2455           ...                                                       // Do what you want now to save the ship!
2456           }
2457         }
2458       \endcode
2459   **/
2460   struct CImgException : public std::exception {
2461 #define _cimg_exception_err(etype,disp_flag) \
2462   std::va_list ap, ap2; \
2463   va_start(ap,format); va_start(ap2,format); \
2464   int size = cimg_vsnprintf(0,0,format,ap2); \
2465   if (size++>=0) { \
2466     delete[] _message; \
2467     _message = new char[size]; \
2468     cimg_vsnprintf(_message,size,format,ap); \
2469     if (cimg::exception_mode()) { \
2470       std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
2471       if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \
2472       catch (CImgException&) {} \
2473       if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \
2474     } \
2475   } \
2476   va_end(ap); va_end(ap2); \
2477 
2478     char *_message;
CImgExceptionCImgException2479     CImgException() { _message = new char[1]; *_message = 0; }
CImgExceptionCImgException2480     CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); }
CImgExceptionCImgException2481     CImgException(const CImgException& e):std::exception(e) {
2482       const size_t size = std::strlen(e._message);
2483       _message = new char[size + 1];
2484       std::strncpy(_message,e._message,size);
2485       _message[size] = 0;
2486     }
throwCImgException2487     ~CImgException() throw() { delete[] _message; }
2488     CImgException& operator=(const CImgException& e) {
2489       const size_t size = std::strlen(e._message);
2490       _message = new char[size + 1];
2491       std::strncpy(_message,e._message,size);
2492       _message[size] = 0;
2493       return *this;
2494     }
2495     //! Return a C-string containing the error message associated to the thrown exception.
whatCImgException2496     const char *what() const throw() { return _message; }
2497   };
2498 
2499   // The CImgAbortException class is used to throw an exception when
2500   // a computationally-intensive function has been aborted by an external signal.
2501   struct CImgAbortException : public std::exception {
2502     char *_message;
CImgAbortExceptionCImgAbortException2503     CImgAbortException() { _message = new char[1]; *_message = 0; }
CImgAbortExceptionCImgAbortException2504     CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); }
CImgAbortExceptionCImgAbortException2505     CImgAbortException(const CImgAbortException& e):std::exception(e) {
2506       const size_t size = std::strlen(e._message);
2507       _message = new char[size + 1];
2508       std::strncpy(_message,e._message,size);
2509       _message[size] = 0;
2510     }
throwCImgAbortException2511     ~CImgAbortException() throw() { delete[] _message; }
2512     CImgAbortException& operator=(const CImgAbortException& e) {
2513       const size_t size = std::strlen(e._message);
2514       _message = new char[size + 1];
2515       std::strncpy(_message,e._message,size);
2516       _message[size] = 0;
2517       return *this;
2518     }
2519     //! Return a C-string containing the error message associated to the thrown exception.
whatCImgAbortException2520     const char *what() const throw() { return _message; }
2521   };
2522 
2523   // The CImgArgumentException class is used to throw an exception related
2524   // to invalid arguments encountered in a library function call.
2525   struct CImgArgumentException : public CImgException {
CImgArgumentExceptionCImgArgumentException2526     CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
2527   };
2528 
2529   // The CImgDisplayException class is used to throw an exception related
2530   // to display problems encountered in a library function call.
2531   struct CImgDisplayException : public CImgException {
CImgDisplayExceptionCImgDisplayException2532     CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); }
2533   };
2534 
2535   // The CImgInstanceException class is used to throw an exception related
2536   // to an invalid instance encountered in a library function call.
2537   struct CImgInstanceException : public CImgException {
CImgInstanceExceptionCImgInstanceException2538     CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
2539   };
2540 
2541   // The CImgIOException class is used to throw an exception related
2542   // to input/output file problems encountered in a library function call.
2543   struct CImgIOException : public CImgException {
CImgIOExceptionCImgIOException2544     CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
2545   };
2546 
2547   // The CImgWarningException class is used to throw an exception for warnings
2548   // encountered in a library function call.
2549   struct CImgWarningException : public CImgException {
CImgWarningExceptionCImgWarningException2550     CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
2551   };
2552 
2553   /*-------------------------------------
2554     #
2555     # Define cimg:: namespace
2556     #
2557     -----------------------------------*/
2558   //! Contains \a low-level functions and variables of the \CImg Library.
2559   /**
2560      Most of the functions and variables within this namespace are used by the \CImg library for low-level operations.
2561      You may use them to access specific const values or environment variables internally used by \CImg.
2562      \warning Never write <tt>using namespace cimg_library::cimg;</tt> in your source code. Lot of functions in the
2563      <tt>cimg:: namespace</tt> have the same names as standard C functions that may be defined in the global
2564      namespace <tt>::</tt>.
2565   **/
2566   namespace cimg {
2567 
2568     // Define traits that will be used to determine the best data type to work in CImg functions.
2569     //
2570     template<typename T> struct type {
stringtype2571       static const char* string() {
2572         static const char* s[] = { "unknown",   "unknown8",   "unknown16",  "unknown24",
2573                                    "unknown32", "unknown40",  "unknown48",  "unknown56",
2574                                    "unknown64", "unknown72",  "unknown80",  "unknown88",
2575                                    "unknown96", "unknown104", "unknown112", "unknown120",
2576                                    "unknown128" };
2577         return s[(sizeof(T)<17)?sizeof(T):0];
2578       }
is_floattype2579       static bool is_float() { return false; }
is_inftype2580       static bool is_inf(const T) { return false; }
is_nantype2581       static bool is_nan(const T) { return false; }
mintype2582       static T min() { return ~max(); }
maxtype2583       static T max() { return (T)1<<(8*sizeof(T) - 1); }
inftype2584       static T inf() { return max(); }
cuttype2585       static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
formattype2586       static const char* format() { return "%s"; }
format_stype2587       static const char* format_s() { return "%s"; }
formattype2588       static const char* format(const T& val) { static const char *const s = "unknown"; cimg::unused(val); return s; }
2589     };
2590 
2591     template<> struct type<bool> {
2592       static const char* string() { static const char *const s = "bool"; return s; }
2593       static bool is_float() { return false; }
2594       static bool is_inf(const bool) { return false; }
2595       static bool is_nan(const bool) { return false; }
2596       static bool min() { return false; }
2597       static bool max() { return true; }
2598       static bool inf() { return max(); }
2599       static bool is_inf() { return false; }
2600       static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
2601       static const char* format() { return "%s"; }
2602       static const char* format_s() { return "%s"; }
2603       static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
2604     };
2605 
2606     template<> struct type<unsigned char> {
2607       static const char* string() { static const char *const s = "unsigned char"; return s; }
2608       static bool is_float() { return false; }
2609       static bool is_inf(const unsigned char) { return false; }
2610       static bool is_nan(const unsigned char) { return false; }
2611       static unsigned char min() { return 0; }
2612       static unsigned char max() { return (unsigned char)-1; }
2613       static unsigned char inf() { return max(); }
2614       static unsigned char cut(const double val) {
2615         return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
2616       static const char* format() { return "%u"; }
2617       static const char* format_s() { return "%u"; }
2618       static unsigned int format(const unsigned char val) { return (unsigned int)val; }
2619     };
2620 
2621 #if defined(CHAR_MAX) && CHAR_MAX==255
2622     template<> struct type<char> {
2623       static const char* string() { static const char *const s = "char"; return s; }
2624       static bool is_float() { return false; }
2625       static bool is_inf(const char) { return false; }
2626       static bool is_nan(const char) { return false; }
2627       static char min() { return 0; }
2628       static char max() { return (char)-1; }
2629       static char inf() { return max(); }
2630       static char cut(const double val) {
2631         return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
2632       static const char* format() { return "%u"; }
2633       static const char* format_s() { return "%u"; }
2634       static unsigned int format(const char val) { return (unsigned int)val; }
2635     };
2636 #else
2637     template<> struct type<char> {
2638       static const char* string() { static const char *const s = "char"; return s; }
2639       static bool is_float() { return false; }
2640       static bool is_inf(const char) { return false; }
2641       static bool is_nan(const char) { return false; }
2642       static char min() { return ~max(); }
2643       static char max() { return (char)((unsigned char)-1>>1); }
2644       static char inf() { return max(); }
2645       static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
2646       static const char* format() { return "%d"; }
2647       static const char* format_s() { return "%d"; }
2648       static int format(const char val) { return (int)val; }
2649     };
2650 #endif
2651 
2652     template<> struct type<signed char> {
2653       static const char* string() { static const char *const s = "signed char"; return s; }
2654       static bool is_float() { return false; }
2655       static bool is_inf(const signed char) { return false; }
2656       static bool is_nan(const signed char) { return false; }
2657       static signed char min() { return ~max(); }
2658       static signed char max() { return (signed char)((unsigned char)-1>>1); }
2659       static signed char inf() { return max(); }
2660       static signed char cut(const double val) {
2661         return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
2662       static const char* format() { return "%d"; }
2663       static const char* format_s() { return "%d"; }
2664       static int format(const signed char val) { return (int)val; }
2665     };
2666 
2667     template<> struct type<unsigned short> {
2668       static const char* string() { static const char *const s = "unsigned short"; return s; }
2669       static bool is_float() { return false; }
2670       static bool is_inf(const unsigned short) { return false; }
2671       static bool is_nan(const unsigned short) { return false; }
2672       static unsigned short min() { return 0; }
2673       static unsigned short max() { return (unsigned short)-1; }
2674       static unsigned short inf() { return max(); }
2675       static unsigned short cut(const double val) {
2676         return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
2677       static const char* format() { return "%u"; }
2678       static const char* format_s() { return "%u"; }
2679       static unsigned int format(const unsigned short val) { return (unsigned int)val; }
2680     };
2681 
2682     template<> struct type<short> {
2683       static const char* string() { static const char *const s = "short"; return s; }
2684       static bool is_float() { return false; }
2685       static bool is_inf(const short) { return false; }
2686       static bool is_nan(const short) { return false; }
2687       static short min() { return ~max(); }
2688       static short max() { return (short)((unsigned short)-1>>1); }
2689       static short inf() { return max(); }
2690       static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
2691       static const char* format() { return "%d"; }
2692       static const char* format_s() { return "%d"; }
2693       static int format(const short val) { return (int)val; }
2694     };
2695 
2696     template<> struct type<unsigned int> {
2697       static const char* string() { static const char *const s = "unsigned int"; return s; }
2698       static bool is_float() { return false; }
2699       static bool is_inf(const unsigned int) { return false; }
2700       static bool is_nan(const unsigned int) { return false; }
2701       static unsigned int min() { return 0; }
2702       static unsigned int max() { return (unsigned int)-1; }
2703       static unsigned int inf() { return max(); }
2704       static unsigned int cut(const double val) {
2705         return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
2706       static const char* format() { return "%u"; }
2707       static const char* format_s() { return "%u"; }
2708       static unsigned int format(const unsigned int val) { return val; }
2709     };
2710 
2711     template<> struct type<int> {
2712       static const char* string() { static const char *const s = "int"; return s; }
2713       static bool is_float() { return false; }
2714       static bool is_inf(const int) { return false; }
2715       static bool is_nan(const int) { return false; }
2716       static int min() { return ~max(); }
2717       static int max() { return (int)((unsigned int)-1>>1); }
2718       static int inf() { return max(); }
2719       static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
2720       static const char* format() { return "%d"; }
2721       static const char* format_s() { return "%d"; }
2722       static int format(const int val) { return val; }
2723     };
2724 
2725     template<> struct type<cimg_uint64> {
2726       static const char* string() { static const char *const s = "unsigned int64"; return s; }
2727       static bool is_float() { return false; }
2728       static bool is_inf(const cimg_uint64) { return false; }
2729       static bool is_nan(const cimg_uint64) { return false; }
2730       static cimg_uint64 min() { return 0; }
2731       static cimg_uint64 max() { return (cimg_uint64)-1; }
2732       static cimg_uint64 inf() { return max(); }
2733       static cimg_uint64 cut(const double val) {
2734         return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; }
2735       static const char* format() { return cimg_fuint64; }
2736       static const char* format_s() { return cimg_fuint64; }
2737       static unsigned long format(const cimg_uint64 val) { return (unsigned long)val; }
2738     };
2739 
2740     template<> struct type<cimg_int64> {
2741       static const char* string() { static const char *const s = "int64"; return s; }
2742       static bool is_float() { return false; }
2743       static bool is_inf(const cimg_int64) { return false; }
2744       static bool is_nan(const cimg_int64) { return false; }
2745       static cimg_int64 min() { return ~max(); }
2746       static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); }
2747       static cimg_int64 inf() { return max(); }
2748       static cimg_int64 cut(const double val) {
2749         return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
2750       }
2751       static const char* format() { return cimg_fint64; }
2752       static const char* format_s() { return cimg_fint64; }
2753       static long format(const long val) { return (long)val; }
2754     };
2755 
2756     template<> struct type<double> {
2757       static const char* string() { static const char *const s = "double"; return s; }
2758       static bool is_float() { return true; }
2759       static bool is_inf(const double val) {
2760 #ifdef isinf
2761         return (bool)isinf(val);
2762 #else
2763         return !is_nan(val) && (val<cimg::type<double>::min() || val>cimg::type<double>::max());
2764 #endif
2765       }
2766       static bool is_nan(const double val) { // Custom version that works with '-ffast-math'
2767         if (sizeof(double)==8) {
2768           cimg_uint64 u;
2769           std::memcpy(&u,&val,sizeof(double));
2770           return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000;
2771         }
2772 #ifdef isnan
2773         return (bool)isnan(val);
2774 #else
2775         return !(val==val);
2776 #endif
2777       }
2778       static double min() { return -DBL_MAX; }
2779       static double max() { return DBL_MAX; }
2780       static double inf() {
2781 #ifdef INFINITY
2782         return (double)INFINITY;
2783 #else
2784         return max()*max();
2785 #endif
2786       }
2787       static double nan() {
2788 #ifdef NAN
2789         return (double)NAN;
2790 #else
2791         const double val_nan = -std::sqrt(-1.0); return val_nan;
2792 #endif
2793       }
2794       static double cut(const double val) { return val; }
2795       static const char* format() { return "%.17g"; }
2796       static const char* format_s() { return "%g"; }
2797       static double format(const double val) { return val; }
2798     };
2799 
2800     template<> struct type<float> {
2801       static const char* string() { static const char *const s = "float"; return s; }
2802       static bool is_float() { return true; }
2803       static bool is_inf(const float val) {
2804 #ifdef isinf
2805         return (bool)isinf(val);
2806 #else
2807         return !is_nan(val) && (val<cimg::type<float>::min() || val>cimg::type<float>::max());
2808 #endif
2809       }
2810       static bool is_nan(const float val) { // Custom version that works with '-ffast-math'
2811         if (sizeof(float)==4) {
2812           unsigned int u;
2813           std::memcpy(&u,&val,sizeof(float));
2814           return (u&0x7fffffff)>0x7f800000;
2815         }
2816 #ifdef isnan
2817         return (bool)isnan(val);
2818 #else
2819         return !(val==val);
2820 #endif
2821       }
2822       static float min() { return -FLT_MAX; }
2823       static float max() { return FLT_MAX; }
2824       static float inf() { return (float)cimg::type<double>::inf(); }
2825       static float nan() { return (float)cimg::type<double>::nan(); }
2826       static float cut(const double val) { return (float)val; }
2827       static float cut(const float val) { return (float)val; }
2828       static const char* format() { return "%.9g"; }
2829       static const char* format_s() { return "%g"; }
2830       static double format(const float val) { return (double)val; }
2831     };
2832 
2833     template<> struct type<long double> {
2834       static const char* string() { static const char *const s = "long double"; return s; }
2835       static bool is_float() { return true; }
2836       static bool is_inf(const long double val) {
2837 #ifdef isinf
2838         return (bool)isinf(val);
2839 #else
2840         return !is_nan(val) && (val<cimg::type<long double>::min() || val>cimg::type<long double>::max());
2841 #endif
2842       }
2843       static bool is_nan(const long double val) {
2844 #ifdef isnan
2845         return (bool)isnan(val);
2846 #else
2847         return !(val==val);
2848 #endif
2849       }
2850       static long double min() { return -LDBL_MAX; }
2851       static long double max() { return LDBL_MAX; }
2852       static long double inf() { return max()*max(); }
2853       static long double nan() { const long double val_nan = -std::sqrt(-1.0L); return val_nan; }
2854       static long double cut(const long double val) { return val; }
2855       static const char* format() { return "%.17g"; }
2856       static const char* format_s() { return "%g"; }
2857       static double format(const long double val) { return (double)val; }
2858     };
2859 
2860 #ifdef cimg_use_half
2861     template<> struct type<half> {
2862       static const char* string() { static const char *const s = "half"; return s; }
2863       static bool is_float() { return true; }
2864       static bool is_inf(const long double val) {
2865 #ifdef isinf
2866         return (bool)isinf(val);
2867 #else
2868         return !is_nan(val) && (val<cimg::type<half>::min() || val>cimg::type<half>::max());
2869 #endif
2870       }
2871       static bool is_nan(const half val) { // Custom version that works with '-ffast-math'
2872         if (sizeof(half)==2) {
2873           short u;
2874           std::memcpy(&u,&val,sizeof(short));
2875           return (bool)((u&0x7fff)>0x7c00);
2876         }
2877         return cimg::type<float>::is_nan((float)val);
2878       }
2879       static half min() { return (half)-65504; }
2880       static half max() { return (half)65504; }
2881       static half inf() { return max()*max(); }
2882       static half nan() { const half val_nan = (half)-std::sqrt(-1.0); return val_nan; }
2883       static half cut(const double val) { return (half)val; }
2884       static const char* format() { return "%.9g"; }
2885       static const char* format_s() { return "%g"; }
2886       static double format(const half val) { return (double)val; }
2887     };
2888 #endif
2889 
2890     template<typename T, typename t> struct superset { typedef T type; };
2891     template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
2892     template<> struct superset<bool,char> { typedef char type; };
2893     template<> struct superset<bool,signed char> { typedef signed char type; };
2894     template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
2895     template<> struct superset<bool,short> { typedef short type; };
2896     template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
2897     template<> struct superset<bool,int> { typedef int type; };
2898     template<> struct superset<bool,cimg_uint64> { typedef cimg_uint64 type; };
2899     template<> struct superset<bool,cimg_int64> { typedef cimg_int64 type; };
2900     template<> struct superset<bool,float> { typedef float type; };
2901     template<> struct superset<bool,double> { typedef double type; };
2902     template<> struct superset<unsigned char,char> { typedef short type; };
2903     template<> struct superset<unsigned char,signed char> { typedef short type; };
2904     template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
2905     template<> struct superset<unsigned char,short> { typedef short type; };
2906     template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
2907     template<> struct superset<unsigned char,int> { typedef int type; };
2908     template<> struct superset<unsigned char,cimg_uint64> { typedef cimg_uint64 type; };
2909     template<> struct superset<unsigned char,cimg_int64> { typedef cimg_int64 type; };
2910     template<> struct superset<unsigned char,float> { typedef float type; };
2911     template<> struct superset<unsigned char,double> { typedef double type; };
2912     template<> struct superset<signed char,unsigned char> { typedef short type; };
2913     template<> struct superset<signed char,char> { typedef short type; };
2914     template<> struct superset<signed char,unsigned short> { typedef int type; };
2915     template<> struct superset<signed char,short> { typedef short type; };
2916     template<> struct superset<signed char,unsigned int> { typedef cimg_int64 type; };
2917     template<> struct superset<signed char,int> { typedef int type; };
2918     template<> struct superset<signed char,cimg_uint64> { typedef cimg_int64 type; };
2919     template<> struct superset<signed char,cimg_int64> { typedef cimg_int64 type; };
2920     template<> struct superset<signed char,float> { typedef float type; };
2921     template<> struct superset<signed char,double> { typedef double type; };
2922     template<> struct superset<char,unsigned char> { typedef short type; };
2923     template<> struct superset<char,signed char> { typedef short type; };
2924     template<> struct superset<char,unsigned short> { typedef int type; };
2925     template<> struct superset<char,short> { typedef short type; };
2926     template<> struct superset<char,unsigned int> { typedef cimg_int64 type; };
2927     template<> struct superset<char,int> { typedef int type; };
2928     template<> struct superset<char,cimg_uint64> { typedef cimg_int64 type; };
2929     template<> struct superset<char,cimg_int64> { typedef cimg_int64 type; };
2930     template<> struct superset<char,float> { typedef float type; };
2931     template<> struct superset<char,double> { typedef double type; };
2932     template<> struct superset<unsigned short,char> { typedef int type; };
2933     template<> struct superset<unsigned short,signed char> { typedef int type; };
2934     template<> struct superset<unsigned short,short> { typedef int type; };
2935     template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
2936     template<> struct superset<unsigned short,int> { typedef int type; };
2937     template<> struct superset<unsigned short,cimg_uint64> { typedef cimg_uint64 type; };
2938     template<> struct superset<unsigned short,cimg_int64> { typedef cimg_int64 type; };
2939     template<> struct superset<unsigned short,float> { typedef float type; };
2940     template<> struct superset<unsigned short,double> { typedef double type; };
2941     template<> struct superset<short,unsigned short> { typedef int type; };
2942     template<> struct superset<short,unsigned int> { typedef cimg_int64 type; };
2943     template<> struct superset<short,int> { typedef int type; };
2944     template<> struct superset<short,cimg_uint64> { typedef cimg_int64 type; };
2945     template<> struct superset<short,cimg_int64> { typedef cimg_int64 type; };
2946     template<> struct superset<short,float> { typedef float type; };
2947     template<> struct superset<short,double> { typedef double type; };
2948     template<> struct superset<unsigned int,char> { typedef cimg_int64 type; };
2949     template<> struct superset<unsigned int,signed char> { typedef cimg_int64 type; };
2950     template<> struct superset<unsigned int,short> { typedef cimg_int64 type; };
2951     template<> struct superset<unsigned int,int> { typedef cimg_int64 type; };
2952     template<> struct superset<unsigned int,cimg_uint64> { typedef cimg_uint64 type; };
2953     template<> struct superset<unsigned int,cimg_int64> { typedef cimg_int64 type; };
2954     template<> struct superset<unsigned int,float> { typedef float type; };
2955     template<> struct superset<unsigned int,double> { typedef double type; };
2956     template<> struct superset<int,unsigned int> { typedef cimg_int64 type; };
2957     template<> struct superset<int,cimg_uint64> { typedef cimg_int64 type; };
2958     template<> struct superset<int,cimg_int64> { typedef cimg_int64 type; };
2959     template<> struct superset<int,float> { typedef float type; };
2960     template<> struct superset<int,double> { typedef double type; };
2961     template<> struct superset<cimg_uint64,char> { typedef cimg_int64 type; };
2962     template<> struct superset<cimg_uint64,signed char> { typedef cimg_int64 type; };
2963     template<> struct superset<cimg_uint64,short> { typedef cimg_int64 type; };
2964     template<> struct superset<cimg_uint64,int> { typedef cimg_int64 type; };
2965     template<> struct superset<cimg_uint64,cimg_int64> { typedef cimg_int64 type; };
2966     template<> struct superset<cimg_uint64,float> { typedef double type; };
2967     template<> struct superset<cimg_uint64,double> { typedef double type; };
2968     template<> struct superset<cimg_int64,float> { typedef double type; };
2969     template<> struct superset<cimg_int64,double> { typedef double type; };
2970     template<> struct superset<float,double> { typedef double type; };
2971 #ifdef cimg_use_half
2972     template<> struct superset<half,unsigned short> { typedef float type; };
2973     template<> struct superset<half,short> { typedef float type; };
2974     template<> struct superset<half,unsigned int> { typedef float type; };
2975     template<> struct superset<half,int> { typedef float type; };
2976     template<> struct superset<half,cimg_uint64> { typedef float type; };
2977     template<> struct superset<half,cimg_int64> { typedef float type; };
2978     template<> struct superset<half,float> { typedef float type; };
2979     template<> struct superset<half,double> { typedef double type; };
2980 #endif
2981 
2982     template<typename t1, typename t2, typename t3> struct superset2 {
2983       typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
2984     };
2985 
2986     template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
2987       typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
2988     };
2989 
2990     template<typename t1, typename t2> struct last { typedef t2 type; };
2991 
2992 #define _cimg_Tt typename cimg::superset<T,t>::type
2993 #define _cimg_Tfloat typename cimg::superset<T,float>::type
2994 #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
2995 #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
2996 
2997     // Define variables used internally by CImg.
2998 #if cimg_display==1
2999     struct X11_info {
3000       unsigned int nb_wins;
3001       pthread_t *events_thread;
3002       pthread_cond_t wait_event;
3003       pthread_mutex_t wait_event_mutex;
3004       CImgDisplay **wins;
3005       Display *display;
3006       unsigned int nb_bits;
3007       bool is_blue_first;
3008       bool is_shm_enabled;
3009       bool byte_order;
3010 #ifdef cimg_use_xrandr
3011       XRRScreenSize *resolutions;
3012       Rotation curr_rotation;
3013       unsigned int curr_resolution;
3014       unsigned int nb_resolutions;
3015 #endif
3016       X11_info():nb_wins(0),events_thread(0),display(0),
3017                  nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
3018 #ifdef __FreeBSD__
3019         XInitThreads();
3020 #endif
3021         wins = new CImgDisplay*[1024];
3022         pthread_mutex_init(&wait_event_mutex,0);
3023         pthread_cond_init(&wait_event,0);
3024 #ifdef cimg_use_xrandr
3025         resolutions = 0;
3026         curr_rotation = 0;
3027         curr_resolution = nb_resolutions = 0;
3028 #endif
3029       }
3030 
3031       ~X11_info() {
3032         delete[] wins;
3033         /*
3034           if (events_thread) {
3035           pthread_cancel(*events_thread);
3036           delete events_thread;
3037           }
3038           if (display) { } // XCloseDisplay(display); }
3039           pthread_cond_destroy(&wait_event);
3040           pthread_mutex_unlock(&wait_event_mutex);
3041           pthread_mutex_destroy(&wait_event_mutex);
3042         */
3043       }
3044     };
3045 #if defined(cimg_module)
3046     X11_info& X11_attr();
3047 #elif defined(cimg_main)
3048     X11_info& X11_attr() { static X11_info val; return val; }
3049 #else
3050     inline X11_info& X11_attr() { static X11_info val; return val; }
3051 #endif
3052 #define cimg_lock_display() cimg::mutex(15)
3053 #define cimg_unlock_display() cimg::mutex(15,0)
3054 
3055 #elif cimg_display==2
3056     struct Win32_info {
3057       HANDLE wait_event;
3058       Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); }
3059     };
3060 #if defined(cimg_module)
3061     Win32_info& Win32_attr();
3062 #elif defined(cimg_main)
3063     Win32_info& Win32_attr() { static Win32_info val; return val; }
3064 #else
3065     inline Win32_info& Win32_attr() { static Win32_info val; return val; }
3066 #endif
3067 #endif
3068 
3069     struct Mutex_info {
3070 #if cimg_OS==2
3071       HANDLE mutex[32];
3072       Mutex_info() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE,0); }
3073       void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); }
3074       void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); }
3075       int trylock(const unsigned int) { return 0; }
3076 #elif defined(_PTHREAD_H)
3077       pthread_mutex_t mutex[32];
3078       Mutex_info() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); }
3079       void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); }
3080       void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); }
3081       int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); }
3082 #else
3083       Mutex_info() {}
3084       void lock(const unsigned int) {}
3085       void unlock(const unsigned int) {}
3086       int trylock(const unsigned int) { return 0; }
3087 #endif
3088     };
3089 #if defined(cimg_module)
3090     Mutex_info& Mutex_attr();
3091 #elif defined(cimg_main)
3092     Mutex_info& Mutex_attr() { static Mutex_info val; return val; }
3093 #else
3094     inline Mutex_info& Mutex_attr() { static Mutex_info val; return val; }
3095 #endif
3096 
3097 #if defined(cimg_use_magick)
3098     static struct Magick_info {
3099       Magick_info() {
3100         Magick::InitializeMagick("");
3101       }
3102     } _Magick_info;
3103 #endif
3104 
3105 #if cimg_display==1
3106     // Define keycodes for X11-based graphical systems.
3107     const unsigned int keyESC        = XK_Escape;
3108     const unsigned int keyF1         = XK_F1;
3109     const unsigned int keyF2         = XK_F2;
3110     const unsigned int keyF3         = XK_F3;
3111     const unsigned int keyF4         = XK_F4;
3112     const unsigned int keyF5         = XK_F5;
3113     const unsigned int keyF6         = XK_F6;
3114     const unsigned int keyF7         = XK_F7;
3115     const unsigned int keyF8         = XK_F8;
3116     const unsigned int keyF9         = XK_F9;
3117     const unsigned int keyF10        = XK_F10;
3118     const unsigned int keyF11        = XK_F11;
3119     const unsigned int keyF12        = XK_F12;
3120     const unsigned int keyPAUSE      = XK_Pause;
3121     const unsigned int key1          = XK_1;
3122     const unsigned int key2          = XK_2;
3123     const unsigned int key3          = XK_3;
3124     const unsigned int key4          = XK_4;
3125     const unsigned int key5          = XK_5;
3126     const unsigned int key6          = XK_6;
3127     const unsigned int key7          = XK_7;
3128     const unsigned int key8          = XK_8;
3129     const unsigned int key9          = XK_9;
3130     const unsigned int key0          = XK_0;
3131     const unsigned int keyBACKSPACE  = XK_BackSpace;
3132     const unsigned int keyINSERT     = XK_Insert;
3133     const unsigned int keyHOME       = XK_Home;
3134     const unsigned int keyPAGEUP     = XK_Page_Up;
3135     const unsigned int keyTAB        = XK_Tab;
3136     const unsigned int keyQ          = XK_q;
3137     const unsigned int keyW          = XK_w;
3138     const unsigned int keyE          = XK_e;
3139     const unsigned int keyR          = XK_r;
3140     const unsigned int keyT          = XK_t;
3141     const unsigned int keyY          = XK_y;
3142     const unsigned int keyU          = XK_u;
3143     const unsigned int keyI          = XK_i;
3144     const unsigned int keyO          = XK_o;
3145     const unsigned int keyP          = XK_p;
3146     const unsigned int keyDELETE     = XK_Delete;
3147     const unsigned int keyEND        = XK_End;
3148     const unsigned int keyPAGEDOWN   = XK_Page_Down;
3149     const unsigned int keyCAPSLOCK   = XK_Caps_Lock;
3150     const unsigned int keyA          = XK_a;
3151     const unsigned int keyS          = XK_s;
3152     const unsigned int keyD          = XK_d;
3153     const unsigned int keyF          = XK_f;
3154     const unsigned int keyG          = XK_g;
3155     const unsigned int keyH          = XK_h;
3156     const unsigned int keyJ          = XK_j;
3157     const unsigned int keyK          = XK_k;
3158     const unsigned int keyL          = XK_l;
3159     const unsigned int keyENTER      = XK_Return;
3160     const unsigned int keySHIFTLEFT  = XK_Shift_L;
3161     const unsigned int keyZ          = XK_z;
3162     const unsigned int keyX          = XK_x;
3163     const unsigned int keyC          = XK_c;
3164     const unsigned int keyV          = XK_v;
3165     const unsigned int keyB          = XK_b;
3166     const unsigned int keyN          = XK_n;
3167     const unsigned int keyM          = XK_m;
3168     const unsigned int keySHIFTRIGHT = XK_Shift_R;
3169     const unsigned int keyARROWUP    = XK_Up;
3170     const unsigned int keyCTRLLEFT   = XK_Control_L;
3171     const unsigned int keyAPPLEFT    = XK_Super_L;
3172     const unsigned int keyALT        = XK_Alt_L;
3173     const unsigned int keySPACE      = XK_space;
3174     const unsigned int keyALTGR      = XK_Alt_R;
3175     const unsigned int keyAPPRIGHT   = XK_Super_R;
3176     const unsigned int keyMENU       = XK_Menu;
3177     const unsigned int keyCTRLRIGHT  = XK_Control_R;
3178     const unsigned int keyARROWLEFT  = XK_Left;
3179     const unsigned int keyARROWDOWN  = XK_Down;
3180     const unsigned int keyARROWRIGHT = XK_Right;
3181     const unsigned int keyPAD0       = XK_KP_0;
3182     const unsigned int keyPAD1       = XK_KP_1;
3183     const unsigned int keyPAD2       = XK_KP_2;
3184     const unsigned int keyPAD3       = XK_KP_3;
3185     const unsigned int keyPAD4       = XK_KP_4;
3186     const unsigned int keyPAD5       = XK_KP_5;
3187     const unsigned int keyPAD6       = XK_KP_6;
3188     const unsigned int keyPAD7       = XK_KP_7;
3189     const unsigned int keyPAD8       = XK_KP_8;
3190     const unsigned int keyPAD9       = XK_KP_9;
3191     const unsigned int keyPADADD     = XK_KP_Add;
3192     const unsigned int keyPADSUB     = XK_KP_Subtract;
3193     const unsigned int keyPADMUL     = XK_KP_Multiply;
3194     const unsigned int keyPADDIV     = XK_KP_Divide;
3195 
3196 #elif cimg_display==2
3197     // Define keycodes for Windows.
3198     const unsigned int keyESC        = VK_ESCAPE;
3199     const unsigned int keyF1         = VK_F1;
3200     const unsigned int keyF2         = VK_F2;
3201     const unsigned int keyF3         = VK_F3;
3202     const unsigned int keyF4         = VK_F4;
3203     const unsigned int keyF5         = VK_F5;
3204     const unsigned int keyF6         = VK_F6;
3205     const unsigned int keyF7         = VK_F7;
3206     const unsigned int keyF8         = VK_F8;
3207     const unsigned int keyF9         = VK_F9;
3208     const unsigned int keyF10        = VK_F10;
3209     const unsigned int keyF11        = VK_F11;
3210     const unsigned int keyF12        = VK_F12;
3211     const unsigned int keyPAUSE      = VK_PAUSE;
3212     const unsigned int key1          = '1';
3213     const unsigned int key2          = '2';
3214     const unsigned int key3          = '3';
3215     const unsigned int key4          = '4';
3216     const unsigned int key5          = '5';
3217     const unsigned int key6          = '6';
3218     const unsigned int key7          = '7';
3219     const unsigned int key8          = '8';
3220     const unsigned int key9          = '9';
3221     const unsigned int key0          = '0';
3222     const unsigned int keyBACKSPACE  = VK_BACK;
3223     const unsigned int keyINSERT     = VK_INSERT;
3224     const unsigned int keyHOME       = VK_HOME;
3225     const unsigned int keyPAGEUP     = VK_PRIOR;
3226     const unsigned int keyTAB        = VK_TAB;
3227     const unsigned int keyQ          = 'Q';
3228     const unsigned int keyW          = 'W';
3229     const unsigned int keyE          = 'E';
3230     const unsigned int keyR          = 'R';
3231     const unsigned int keyT          = 'T';
3232     const unsigned int keyY          = 'Y';
3233     const unsigned int keyU          = 'U';
3234     const unsigned int keyI          = 'I';
3235     const unsigned int keyO          = 'O';
3236     const unsigned int keyP          = 'P';
3237     const unsigned int keyDELETE     = VK_DELETE;
3238     const unsigned int keyEND        = VK_END;
3239     const unsigned int keyPAGEDOWN   = VK_NEXT;
3240     const unsigned int keyCAPSLOCK   = VK_CAPITAL;
3241     const unsigned int keyA          = 'A';
3242     const unsigned int keyS          = 'S';
3243     const unsigned int keyD          = 'D';
3244     const unsigned int keyF          = 'F';
3245     const unsigned int keyG          = 'G';
3246     const unsigned int keyH          = 'H';
3247     const unsigned int keyJ          = 'J';
3248     const unsigned int keyK          = 'K';
3249     const unsigned int keyL          = 'L';
3250     const unsigned int keyENTER      = VK_RETURN;
3251     const unsigned int keySHIFTLEFT  = VK_SHIFT;
3252     const unsigned int keyZ          = 'Z';
3253     const unsigned int keyX          = 'X';
3254     const unsigned int keyC          = 'C';
3255     const unsigned int keyV          = 'V';
3256     const unsigned int keyB          = 'B';
3257     const unsigned int keyN          = 'N';
3258     const unsigned int keyM          = 'M';
3259     const unsigned int keySHIFTRIGHT = VK_SHIFT;
3260     const unsigned int keyARROWUP    = VK_UP;
3261     const unsigned int keyCTRLLEFT   = VK_CONTROL;
3262     const unsigned int keyAPPLEFT    = VK_LWIN;
3263     const unsigned int keyALT        = VK_LMENU;
3264     const unsigned int keySPACE      = VK_SPACE;
3265     const unsigned int keyALTGR      = VK_CONTROL;
3266     const unsigned int keyAPPRIGHT   = VK_RWIN;
3267     const unsigned int keyMENU       = VK_APPS;
3268     const unsigned int keyCTRLRIGHT  = VK_CONTROL;
3269     const unsigned int keyARROWLEFT  = VK_LEFT;
3270     const unsigned int keyARROWDOWN  = VK_DOWN;
3271     const unsigned int keyARROWRIGHT = VK_RIGHT;
3272     const unsigned int keyPAD0       = 0x60;
3273     const unsigned int keyPAD1       = 0x61;
3274     const unsigned int keyPAD2       = 0x62;
3275     const unsigned int keyPAD3       = 0x63;
3276     const unsigned int keyPAD4       = 0x64;
3277     const unsigned int keyPAD5       = 0x65;
3278     const unsigned int keyPAD6       = 0x66;
3279     const unsigned int keyPAD7       = 0x67;
3280     const unsigned int keyPAD8       = 0x68;
3281     const unsigned int keyPAD9       = 0x69;
3282     const unsigned int keyPADADD     = VK_ADD;
3283     const unsigned int keyPADSUB     = VK_SUBTRACT;
3284     const unsigned int keyPADMUL     = VK_MULTIPLY;
3285     const unsigned int keyPADDIV     = VK_DIVIDE;
3286 
3287 #else
3288     // Define random keycodes when no display is available.
3289     // (should rarely be used then!).
3290     const unsigned int keyESC        = 1U;   //!< Keycode for the \c ESC key (architecture-dependent).
3291     const unsigned int keyF1         = 2U;   //!< Keycode for the \c F1 key (architecture-dependent).
3292     const unsigned int keyF2         = 3U;   //!< Keycode for the \c F2 key (architecture-dependent).
3293     const unsigned int keyF3         = 4U;   //!< Keycode for the \c F3 key (architecture-dependent).
3294     const unsigned int keyF4         = 5U;   //!< Keycode for the \c F4 key (architecture-dependent).
3295     const unsigned int keyF5         = 6U;   //!< Keycode for the \c F5 key (architecture-dependent).
3296     const unsigned int keyF6         = 7U;   //!< Keycode for the \c F6 key (architecture-dependent).
3297     const unsigned int keyF7         = 8U;   //!< Keycode for the \c F7 key (architecture-dependent).
3298     const unsigned int keyF8         = 9U;   //!< Keycode for the \c F8 key (architecture-dependent).
3299     const unsigned int keyF9         = 10U;  //!< Keycode for the \c F9 key (architecture-dependent).
3300     const unsigned int keyF10        = 11U;  //!< Keycode for the \c F10 key (architecture-dependent).
3301     const unsigned int keyF11        = 12U;  //!< Keycode for the \c F11 key (architecture-dependent).
3302     const unsigned int keyF12        = 13U;  //!< Keycode for the \c F12 key (architecture-dependent).
3303     const unsigned int keyPAUSE      = 14U;  //!< Keycode for the \c PAUSE key (architecture-dependent).
3304     const unsigned int key1          = 15U;  //!< Keycode for the \c 1 key (architecture-dependent).
3305     const unsigned int key2          = 16U;  //!< Keycode for the \c 2 key (architecture-dependent).
3306     const unsigned int key3          = 17U;  //!< Keycode for the \c 3 key (architecture-dependent).
3307     const unsigned int key4          = 18U;  //!< Keycode for the \c 4 key (architecture-dependent).
3308     const unsigned int key5          = 19U;  //!< Keycode for the \c 5 key (architecture-dependent).
3309     const unsigned int key6          = 20U;  //!< Keycode for the \c 6 key (architecture-dependent).
3310     const unsigned int key7          = 21U;  //!< Keycode for the \c 7 key (architecture-dependent).
3311     const unsigned int key8          = 22U;  //!< Keycode for the \c 8 key (architecture-dependent).
3312     const unsigned int key9          = 23U;  //!< Keycode for the \c 9 key (architecture-dependent).
3313     const unsigned int key0          = 24U;  //!< Keycode for the \c 0 key (architecture-dependent).
3314     const unsigned int keyBACKSPACE  = 25U;  //!< Keycode for the \c BACKSPACE key (architecture-dependent).
3315     const unsigned int keyINSERT     = 26U;  //!< Keycode for the \c INSERT key (architecture-dependent).
3316     const unsigned int keyHOME       = 27U;  //!< Keycode for the \c HOME key (architecture-dependent).
3317     const unsigned int keyPAGEUP     = 28U;  //!< Keycode for the \c PAGEUP key (architecture-dependent).
3318     const unsigned int keyTAB        = 29U;  //!< Keycode for the \c TAB key (architecture-dependent).
3319     const unsigned int keyQ          = 30U;  //!< Keycode for the \c Q key (architecture-dependent).
3320     const unsigned int keyW          = 31U;  //!< Keycode for the \c W key (architecture-dependent).
3321     const unsigned int keyE          = 32U;  //!< Keycode for the \c E key (architecture-dependent).
3322     const unsigned int keyR          = 33U;  //!< Keycode for the \c R key (architecture-dependent).
3323     const unsigned int keyT          = 34U;  //!< Keycode for the \c T key (architecture-dependent).
3324     const unsigned int keyY          = 35U;  //!< Keycode for the \c Y key (architecture-dependent).
3325     const unsigned int keyU          = 36U;  //!< Keycode for the \c U key (architecture-dependent).
3326     const unsigned int keyI          = 37U;  //!< Keycode for the \c I key (architecture-dependent).
3327     const unsigned int keyO          = 38U;  //!< Keycode for the \c O key (architecture-dependent).
3328     const unsigned int keyP          = 39U;  //!< Keycode for the \c P key (architecture-dependent).
3329     const unsigned int keyDELETE     = 40U;  //!< Keycode for the \c DELETE key (architecture-dependent).
3330     const unsigned int keyEND        = 41U;  //!< Keycode for the \c END key (architecture-dependent).
3331     const unsigned int keyPAGEDOWN   = 42U;  //!< Keycode for the \c PAGEDOWN key (architecture-dependent).
3332     const unsigned int keyCAPSLOCK   = 43U;  //!< Keycode for the \c CAPSLOCK key (architecture-dependent).
3333     const unsigned int keyA          = 44U;  //!< Keycode for the \c A key (architecture-dependent).
3334     const unsigned int keyS          = 45U;  //!< Keycode for the \c S key (architecture-dependent).
3335     const unsigned int keyD          = 46U;  //!< Keycode for the \c D key (architecture-dependent).
3336     const unsigned int keyF          = 47U;  //!< Keycode for the \c F key (architecture-dependent).
3337     const unsigned int keyG          = 48U;  //!< Keycode for the \c G key (architecture-dependent).
3338     const unsigned int keyH          = 49U;  //!< Keycode for the \c H key (architecture-dependent).
3339     const unsigned int keyJ          = 50U;  //!< Keycode for the \c J key (architecture-dependent).
3340     const unsigned int keyK          = 51U;  //!< Keycode for the \c K key (architecture-dependent).
3341     const unsigned int keyL          = 52U;  //!< Keycode for the \c L key (architecture-dependent).
3342     const unsigned int keyENTER      = 53U;  //!< Keycode for the \c ENTER key (architecture-dependent).
3343     const unsigned int keySHIFTLEFT  = 54U;  //!< Keycode for the \c SHIFTLEFT key (architecture-dependent).
3344     const unsigned int keyZ          = 55U;  //!< Keycode for the \c Z key (architecture-dependent).
3345     const unsigned int keyX          = 56U;  //!< Keycode for the \c X key (architecture-dependent).
3346     const unsigned int keyC          = 57U;  //!< Keycode for the \c C key (architecture-dependent).
3347     const unsigned int keyV          = 58U;  //!< Keycode for the \c V key (architecture-dependent).
3348     const unsigned int keyB          = 59U;  //!< Keycode for the \c B key (architecture-dependent).
3349     const unsigned int keyN          = 60U;  //!< Keycode for the \c N key (architecture-dependent).
3350     const unsigned int keyM          = 61U;  //!< Keycode for the \c M key (architecture-dependent).
3351     const unsigned int keySHIFTRIGHT = 62U;  //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent).
3352     const unsigned int keyARROWUP    = 63U;  //!< Keycode for the \c ARROWUP key (architecture-dependent).
3353     const unsigned int keyCTRLLEFT   = 64U;  //!< Keycode for the \c CTRLLEFT key (architecture-dependent).
3354     const unsigned int keyAPPLEFT    = 65U;  //!< Keycode for the \c APPLEFT key (architecture-dependent).
3355     const unsigned int keyALT        = 66U;  //!< Keycode for the \c ALT key (architecture-dependent).
3356     const unsigned int keySPACE      = 67U;  //!< Keycode for the \c SPACE key (architecture-dependent).
3357     const unsigned int keyALTGR      = 68U;  //!< Keycode for the \c ALTGR key (architecture-dependent).
3358     const unsigned int keyAPPRIGHT   = 69U;  //!< Keycode for the \c APPRIGHT key (architecture-dependent).
3359     const unsigned int keyMENU       = 70U;  //!< Keycode for the \c MENU key (architecture-dependent).
3360     const unsigned int keyCTRLRIGHT  = 71U;  //!< Keycode for the \c CTRLRIGHT key (architecture-dependent).
3361     const unsigned int keyARROWLEFT  = 72U;  //!< Keycode for the \c ARROWLEFT key (architecture-dependent).
3362     const unsigned int keyARROWDOWN  = 73U;  //!< Keycode for the \c ARROWDOWN key (architecture-dependent).
3363     const unsigned int keyARROWRIGHT = 74U;  //!< Keycode for the \c ARROWRIGHT key (architecture-dependent).
3364     const unsigned int keyPAD0       = 75U;  //!< Keycode for the \c PAD0 key (architecture-dependent).
3365     const unsigned int keyPAD1       = 76U;  //!< Keycode for the \c PAD1 key (architecture-dependent).
3366     const unsigned int keyPAD2       = 77U;  //!< Keycode for the \c PAD2 key (architecture-dependent).
3367     const unsigned int keyPAD3       = 78U;  //!< Keycode for the \c PAD3 key (architecture-dependent).
3368     const unsigned int keyPAD4       = 79U;  //!< Keycode for the \c PAD4 key (architecture-dependent).
3369     const unsigned int keyPAD5       = 80U;  //!< Keycode for the \c PAD5 key (architecture-dependent).
3370     const unsigned int keyPAD6       = 81U;  //!< Keycode for the \c PAD6 key (architecture-dependent).
3371     const unsigned int keyPAD7       = 82U;  //!< Keycode for the \c PAD7 key (architecture-dependent).
3372     const unsigned int keyPAD8       = 83U;  //!< Keycode for the \c PAD8 key (architecture-dependent).
3373     const unsigned int keyPAD9       = 84U;  //!< Keycode for the \c PAD9 key (architecture-dependent).
3374     const unsigned int keyPADADD     = 85U;  //!< Keycode for the \c PADADD key (architecture-dependent).
3375     const unsigned int keyPADSUB     = 86U;  //!< Keycode for the \c PADSUB key (architecture-dependent).
3376     const unsigned int keyPADMUL     = 87U;  //!< Keycode for the \c PADMUL key (architecture-dependent).
3377     const unsigned int keyPADDIV     = 88U;  //!< Keycode for the \c PADDDIV key (architecture-dependent).
3378 #endif
3379 
3380     const double PI = 3.14159265358979323846;   //!< Value of the mathematical constant PI
3381 
3382     // Define a 12x13 font (small size).
3383     static const char *const data_font12x13 =
3384       "                          .wjwlwmyuw>wjwkwbwjwkwRxuwmwjwkwmyuwJwjwlx`w      Fw                      "
3385       "   mwlwlwuwnwuynwuwmyTwlwkwuwmwuwnwlwkwuwmwuw_wuxlwlwkwuwnwuynwuwTwlwlwtwnwtwnw my     Qw   +wlw   b"
3386       "{ \\w  Wx`xTw_w[wbxawSwkw  nynwky<x1w `y    ,w  Xwuw   CxlwiwlwmyuwbwuwUwiwlwbwiwrwqw^wuwmxuwnwiwlwmy"
3387       "uwJwiwlw^wnwEymymymymy1w^wkxnxtxnw<| gybwkwuwjwtwowmxswnxnwkxlxkw:wlymxlymykwn{myo{nymy2ykwqwqwm{myo"
3388       "zn{o{mzpwrwpwkwkwswowkwqwqxswnyozlyozmzp}pwrwqwqwqwswswsxsxqwqwp}qwlwiwjybw`w[wcw_wkwkwkwkw mw\"wlwiw"
3389       "=wtw`xIw awuwlwm{o{mylwn|pwtwtwoy`w_w_wbwiwkxcwqwpwkznwuwjzpyGzqymyaxlylw_zWxkxaxrwqxrwqyswowkwkwkwk"
3390       "wkwkwk}qyo{o{o{o{owkwkwkwkznxswnymymymymyayuwqwrwpwrwpwrwpwrwqwqwpwkwtwlwkwlwuwnwuynwuwmyTwkwlwuwmwu"
3391       "wnwkwlwuwmwuwkxlwuxmwkwlwuwnwuynwuwTwkwlwuwmwuwlwmwkwtwUwuwuwowswowswowswowsw;wqwtw_ymzp~py>w bwswcw"
3392       "kwuwjwuwozpwtwuwnwtwowkwjwmwuwuwkwIxmxuxowuwmwswowswmxnwjwhwowswowsw0wmwowswuwnwrwowswpwswowkwjwrwqw"
3393       "rwpwkwkwtwnwkxsxqxswowswpwswnwswpwswowrwnwmwrwqwqwqwswswrwswowswjwpwlxjwkxuxLw[wcw_wSwkw mw\"wlwiw=wt"
3394       "wmxlwFw cwswnwuwnwkwjwswo{pwrwpwtwtwpwswby`w`yUwlwtwpwqwpwswowlw\\wrwrxuwHwrwfwuwjwlwlwTyuwVwlwtwawsw"
3395       "owswowswcwuwmwuwmwuwmwuwmwuwlwkwuwnwswpwkwkwkwkwkwkwkwkwswoxswowswowswowswowswowswowrwpwswpwrwpwrwpw"
3396       "rwpwrwpwswoznwtw  Ww (wGwtwtwqwqwqwuwuwuwqwswuwqwqw=wqxtw`{nzp~q{ozowrwnxmwtwow bzawkwuwl}rwuwnwtwuw"
3397       "nwtwowkwjwlyjwIwlwswmwiwkwnwuwnwkwhwnwswowswowkwewewixnwsytwswuwnwrwpwkwrwpwkwkwkwrwpwkwkwuwmwkxsxqw"
3398       "uwtwpwqwqwswowqwqwswowiwmwrwpwswowtwtwpwuwmwuwjwowkwjwlxsxXynzmymznyozlzoznwkwkwtwnwkzuyrzmynzmzowux"
3399       "myozmwswpwrwowtwtwrwrwpwrwp{mwlwiwHyuwpwtwkwmxlynzoxswmwmwswnwswowtxq|owtwtwpym{p{owswnwuwmwlwkwqwqx"
3400       "uwuxqwrwpwtwtwqwqwowlwuwuwkwmwlwtwowuwuwdwjznwl{nwuwnwkx_wtxtwswtwlwtwWwuytwgyjwmwjwawswoyuwVwlwtwnw"
3401       "twmwtwnwtwmwuwmwlwuwmwuwmwuwmwuwmwuwmwuwmxuwowkwkwkwkwkwkwkwkwkwrwpwuwtwpwqwqwqwqwqwqwqwqwqwowtwpwsw"
3402       "uwqwrwpwrwpwrwpwrwowuwnwswowuwlymymymymymymyuyqymymymymynwkwkwkwjynzmymymymymykwmzowswowswowswowswpw"
3403       "rwozowrwW}q}qwtwtwqwtwtwqwtwtwA}rwuw_{p~r~r}pwtwowrwnxmwtwow aw_w]wtwpwuwmxuwmybwjwlyjwIwlwswmwiwnyn"
3404       "wtwnznzkwmynwswTyp}pylwmwtwtwtwswuwn{owkwrwp{o{owk|pwkwkxlwkwuwuwuwqwuwtwpwqwqwswowqwqwswoykwmwrwpws"
3405       "wowuwuwuwowkwjwnwkwjwDwowswowkwswowswowkwswowswowkwkwuwmwkwswswswswowswowswowswoxlwswowkwswpwrwowtwt"
3406       "wqwtwowrwlwoxkwhxVxuxpwtypwuwjwnwtwnwkwswowtxnxmwswowqwqwtwuxqwtwnwtwtwqwswowswmwm{nwuwlxnwkwqwqwtwt"
3407       "wqwrwpwtwtwqwuyuwpwiwhwnwmwrwnwbwkwuwlwlwswoxuxowlwtw`wuwrwszmwtwo}dwuwtwuw[}qymx`wswoyuwow_ylxlwtwo"
3408       "yuwoyuwoyuwmwlwuwmwuwmwuwmwuwmwuwmwuwmwt{swk{o{o{o{owkwkwkwlztwpwuwtwpwqwqwqwqwqwqwqwqwqwnxowtwtwqwr"
3409       "wpwrwpwrwpwrwnwmwswowuwiwkwkwkwkwkwkwswswkwswowswowswowswowkwkwkwkwswowswowswowswowswowswowswcwtxows"
3410       "wowswowswowswpwrwowswpwrwWwtwtwqwqwqwuwuwuwqwuwswqwqw>wowuw`}q~q|q}qwrwpwrwowtwnwtwo~ izaw]wtwoykwux"
3411       "qwtwswfwjwmwuwuwn}eyaxlwswmwjwjwpwswjwowswmwmwswnzWy]ypwlwtwtwuwswswowrwpwkwrwpwkwkwsyqwrwpwkwkwuwmw"
3412       "kwuwuwuwqwtwuwpwqwqznwqwqzkynwmwrwowuwnwuwuwuwowkwjwnwkxkwGzowswowkwswo{owkwswowswowkwkxlwkwswswswsw"
3413       "owswowswowswowjxmwkwswowtwnwuwuwuwpxmwtwlwlwlwiwlytwewtwtwqwswowtxoznwswnxmwswnwuwmwuwnwswowtwtwqwtw"
3414       "twqwtwnwtwtwqwswowswmwmwswowswmwmwkwqwqwtwtwqwrwowuwuwpwuyuwq~own~own~owbwkwuwmznwswmwbwswawuwrwgwtw"
3415       "hwdwuytwXwJwswnxuw=wtwmwswowtxowswqxmwswowswowswowswowswowswnwtwowkwkwkwkwkwkwkwkwkwrwpwtwuwpwqwqwqw"
3416       "qwqwqwqwqwqwnxowtwtwqwrwpwrwpwrwpwrwnwmwswowtwmznznznznznzn~swk{o{o{o{owkwkwkwkwswowswowswowswowswow"
3417       "swowswo}qwuwuwowswowswowswowswowtwnwswowtwUwuwuwowswowswowswowsw@}qx`}q~pzo{pwrwpwrwowtwnwtwow aw_w_"
3418       "}owuwmwuwtwrwswuwewjwkwiwJwkwswmwkwiwp|kwowswmwmwswkwWym}mypwlwszr{owrwpwkwrwpwkwkwqwqwrwpwkwkwtwnwk"
3419       "wtwtwqwtwuwpwqwqwkwqwqwtwiwnwmwrwowuwnwuwuwuwpwuwlwkwmwjwkwHwswowswowkwswowkwkwswowswowkwkwuwmwkwsws"
3420       "wswswowswowswowswowhwnwkwswowtwnwuwuwuwpxmwtwmwkwlwiwmwtydwtwtwqwswowswowtwnwswowkwswnwuwnwtwnwswowt"
3421       "wtwqwtwtwqwtwnwtwtwqwswowswmwmwswowswnwlwkwqwqxuwuxqwrwnyowqwpwiwhwpwuwuwowrwpwuwuwdwkwuwlwlwswo{owk"
3422       "xuwawtxtwszmwtwiwdwuwtwuwXwJwswmwuwKzmwtwlwtxowrwpwtxrxl{o{o{o{o{o{o{owkwkwkwkwkwkwkwkwkwrwpwtwuwpwq"
3423       "wqwqwqwqwqwqwqwqwowtwpwuwswqwrwpwrwpwrwpwrwnwmznwswowswowswowswowswowswowswowswowkwkwkwkwkwkwkwkwkws"
3424       "wowswowswowswowswowswowswcwuwuwowswowswowswowswowtwnwswowtwTymymymymy=wmw^wuwuwmxlxmyowrwowtwnwtwmxm"
3425       "w bwswIwuwmwuwmwuwtwrxswdwjw]wJwkxuxmwlwlwswlwjwowswmwmwswlwSycyawlwswowrwowswpwswowkwjwrwqwrwpwkwkw"
3426       "swowkwqwqwsxowswpwjwswpwswowrwnwmxtxnwlwswpwswmwlwlwjwkwHwswowswowkwswowswowkwswowswowkwkwtwnwkwswsw"
3427       "swswowswowswowswowkwswowkwswnxlwswpwtwmxmwjwlwiwTxuxpwtxowswowtwnwswowkwswnynwtwnwswowtwtwqxuwuxqwtw"
3428       "nwtwtwqwswowswmwlwuwnwswowkwjwswo{pwrwmwmwswnwjwiwnymwtwnycwkwuwlwl{mwmwiw_wrwdwtwVwrw*wswmwuw?wtwlw"
3429       "tzqwrwpwtzswkwswowswowswowswowswowswowswnwswpwkwkwkwkwkwkwkwkwswowsxowswowswowswowswowswowrwpwswpxtx"
3430       "pxtxpxtxpxtxnwmwkwswowswowswowswowswowswowswowtxowkwswowswowswowswowkwkwkwkwswowswowswowswowswowswow"
3431       "swlwnxtwowswowswowswowswnxmwswnx >wlw\\wkx`wnwrwoznwtwmxl| gybw^wtwozmwsxpzuxfxlx]wnw_wlxjyn{o{nykwnz"
3432       "mymwkynymwkwewewjwjwrwswqwp{myozn{owizpwrwpwkwkwrwp{owqwqwsxnyowiyowrwozmwlzmwlwswqxsxnwm}qwjxlwGzoz"
3433       "mymznynwjzowswowkwkwswowkwswswswswnynzmzowjymxlznxlwswqwrwnwm{mwlwiwHxuxpzmxlymynwswmwnwrwozmxuxo{pw"
3434       "txn{pzmykwmyo}p{owkyuynwnwrwmwly`w_w_wbwjzo{pwqwnwmwhw_z>zY}M|nwuw2wqwqwryrwqwqyowqwqwqwqwqwqwqwqwqw"
3435       "qwqwqwr{qyo{o{o{o{owkwkwkwkznwsxnymymymymycwuynznznznzmwmwkwuynznznznznznznyuzrymymymymynwkwkwkwjynw"
3436       "swnymymymymybzmznznznznwlzmw     hwHwlwSwTw <w8z ]x tx Zxjwmx RwWw/wgw pw_ynwky=wCwmwaw\\w_wnw  1wIwl"
3437       "z 'wiwuwaw  mw    Pw   swlwjw     hw        f| pyWx/wgw rxSw/wCwmwaw\\w_wnw  1w  AwRx  nw    Pw   txk"
3438       "wlxm";
3439 
3440     // Define a 20x23 font (normal size).
3441     static const char *const data_font20x23 =
3442       "                                                9q\\q^r_rnp`qnq`plp7q\\q^q_qmqbq\\q^q_qmqHqmp_q\\q^r_rnp"
3443       "`qnq7q\\q^q_qmq_q \"r                                                       Mq^q^qnq`pnr`qnq`plp6q^q^p"
3444       "mp`qmqaq^q^pmp`qmqIpmq]q^q^qnq`pnr`qnq6q^q^pmp`qmq`q \"plp         'q     5qmq               Vq      "
3445       "               Xq    [plp      3qYq_p^rnpLplp8qYq_qNqYq_q4rmpaqYq_q_rmp%qYq^pGq  Irc|!pKp]raqjq`p   "
3446       "HtNq_qmq\\plqbp_shpdscq[q^q[p [q]s_r`uau]rbv`tcxbuat LsZucrav_udwcxdw`udqiqeq]q]qjreq]sksgrjqbtcv_tcv"
3447       "aud{eqiqgqfqgqjsjqlrjrhrirfzfs`q[sZqMqJqCqNsLq]q]q]q]q   .scq]s \\sKt%r  [s^raxdxat_qazgqlqlqctJqIqIq"
3448       "LqHsOqiqOtaqmq\\uft nufu`sLs`t\\qKv<r\\rLrepirepitgpeq]r^r^r^r^r^r^{gudxdxdxdxdq]q]q]q]wcrjqbt`t`t`t`tL"
3449       "tlpgqiqeqiqeqiqeqiqgrireq[s_q[q_pnp_pnr`qnq`plp7q[q_s`qmqcq[q_s`qmq]pkpbpmr`q[q_s`pmraqmq8q[q^pnp_qn"
3450       "q^qaq\\qnq !pnqd{!pJp^tdunucr _y  dvOq_qmq\\plpap_pmpipdudq[p\\p_plplp _q^ubtawcw^rbvavdxcwcw Ou]yerawb"
3451       "xeyexdwbxeqiqeq]q]qkrdq]sksgrjqdxewbxewcwe{eqiqfqhqfqjsjqkqjqfqiqezfs`q[s[sMpJqCqOtLq]q]q]q]q  q 1tc"
3452       "q]t ^vaq_w&r  \\u_raxdxcxcuczgqlqlqexMsJqJsMq[p^uPqiqdq]uaqmq]qkqcq!qkqguaqmqNpkp\\p]pKtmp:p]plpKpfpfp"
3453       "fpcpipdq]r^r^r^r^r^r^{ixexdxdxdxdq]q]q]q]yerjqdxdxdxdxdxPwnpfqiqeqiqeqiqeqiqfqiqdq\\u_p[p^pnpKqnq_r5p"
3454       "[p^pmp`qmqbp[p^pmp`qmq]tKp[p^pmpLqmq7p[p]pnp_qnq^p`q\\qnq5uauauauaucq`qhq4p]pKr_ueunucr `q  \\rkpOq_qm"
3455       "q\\plpctbqmqkqerlpdq\\q\\q_qnpnq\\q%q^qkqcqnqapjrdpjr`sbq]rkp^qcrkrerkq Oplr`sirgtbqkrdripeqjsfq]q]ripeq"
3456       "iqeq]q]qlrcq]sksgskqerjrfqkrdrjrfqkrerjp`q`qiqfqhqeqkskqiqlqdqkq\\qeq]qZq\\qmqNqKqCqOqIq5q]q  q 1q`qZq"
3457       " _rlqbtaqjp$q  ^qkqatbr^q]rjrewdqhqgqlqlqfrjrOuKqKu8p_rlpOqkqcq]qFpgpcp\"pgpTpkp\\q^p\\p^qLump:p^pjpLpg"
3458       "pepgpbpjpPt`t`t`t`t`qnq_qnqcripeq]q]q]q]q]q]q]q]qjsfskqerjrfrjrfrjrfrjrfrjrRrjrfqiqeqiqeqiqeqiqeqkqc"
3459       "vbrlq`q]q_plp Iq]q_qmqNq]q_qmqKtIq]q_qmq ^q]q^plpKq`q mqkqcqkqcqkqcqkqcqkqdq`qhq5q^qLt`ueunudtasbqip"
3460       "`q`pipcq  [qIq_qmq`{gvcqmqkpdq_q\\q\\q]rZq%q_rkraqZq]qaqnqbq]qXqcqiqeqiq1pSpXq`qfrhqnqbqjqdq]qhqfq]q]q"
3461       "]qiqeq]q]qmrbq]qnqmqnqgskqeqhqfqjqdqhqfqjqeqYq`qiqfrjreqkskqirnrdrmr]qdq]qZq]qkq)qCqOqIq5q]q  q 1q`q"
3462       "Zq _qkq_qaq mq  ^qkqaqnqar_q]qhqfrnqnreqhqgqlqlqfqhqPwLqLw9p_q_phqdqkqcq]qGplslpiu#pmtlpUpkp\\q_q_r8u"
3463       "mp:p^pjpLpgpepgperipcq^qnq`qnq`qnq`qnq`qnq`qnq`qmqcq]q]q]q]q]q]q]q]q]qhqfskqeqhqfqhqfqhqfqhqfqhqdphp"
3464       "fqirfqiqeqiqeqiqeqiqermrcwcqkq    [q 3qZp Oq nqmqmqeqiqeqiqeqiqeqiqeq_piq4q^pLvatd|evdvcqipasaqkqdq "
3465       " [qHq_qmq`{hrnpmpcqmqlpcq_q\\pZp]rZq%q_qiqaqZq]qapmqbq^qWqcqiqeqiqdq]qUsSs[qaqdqhqnqbqjqeq\\qgqgq]q^q\\"
3466       "qiqeq]q]qnraq]qnqmqnqgqnqlqfqfqgqjqeqfqgqjqeqYq`qiqeqjqdqlqmqlqhqnqbqmq]rdq]qZq^pgp=taqns`s`snqatdv_"
3467       "snqcqnsbq]q]qkqcq]qnsmshqns`saqnsasnqcqnr`tbvaqjqeqiqdqkqkqjrkreqiqdw`q`qZq#tnreqkq^qatauaqnsdqiq`ra"
3468       "qjqdqiqdpmrcxdqmqmqatbxfyeqiqbqnq`r`q^qfqhrmqmrfqhqgqlqlqgqfqep[pnqnp[p`q`pipbpnqnpNq]taq^qnqnqbqmqb"
3469       "q\\qIqmpkpmqkqkp$qmpkpmqVqmq\\q`q[pLqjqeump:p^pjpLphpdphpapkpbq^qnq`qnq`qnq`qnq`qnq`qnq`qmqdq\\q]q]q]q]"
3470       "q]q]q]q]qgqgqnqlqfqfqhqfqhqfqhqfqhqfqfrjrhqiqnqgqiqeqiqeqiqeqiqdqmqbqkrdqmsbt`t`t`t`t`t`tlsfs_t`t`t`"
3471       "tbq]q]q]q[tbqns`s_s_s_s_s\\q`smpdqjqdqjqdqjqdqjqeqiqdqnscqiq;qlqlqgqgqgqnqmqnqgqjqnqgqgqfq_qjq<{fpjpL"
3472       "vatd|fxeqkqdqipasaqkqdp  \\yNqGplqeqmp`qmqmqcrLqZq`qnpnq\\q%q_qiqaqZq^rbqmqbubqms^qaqkqdqiqdq]qXuf{fu_"
3473       "q`qlrnqlqjqlqcqkreq\\qgqgq]q^q\\qiqeq]q]t`q]qnqmqnqgqnqlqfqfqgqkreqfqgqkres[q`qiqeqjqdqlqmqlqhs`s]rcq]"
3474       "qZq#vbwcvbwcwev`wcwcq]q]qlqbq]vnthwcwcwcwcubwcvaqjqdqkqcqkqkqiqkqdqiqdw`q`qZq7smsfxdqlr^qavdvawdqkq_"
3475       "raqjqdpgpeqntdxdqmqmqcwdyfyeqiqcqlq`raq^qfqhqlqlqfqhqgqlqlqgqfqfrZqZraqarkraqLq^vbq^wbqmqbq]tKpmpfpk"
3476       "pjp_plp9plpkplpUs[qaqZpLqjqeump:p^pjpaplp_piqdpiqaplqbq_qlqbqlqbqlqbqlqbqlqbqlqbrmqdq\\q]q]q]q]q]q]q]"
3477       "q]qgqgqnqlqfqfqhqfqhqfqhqfqhqfqerlrgqjqmqgqiqeqiqeqiqeqiqcsaqjqdqnq`vbvbvbvbvbvbvnuivbwcwcwcwcq]q]q]"
3478       "q]wcwcwcwcwcwcwOwcqjqdqjqdqjqdqjqeqiqdwdqiq;pkqkpgpepgpmumpgpjrmpgpepfq_qkq;{hrkpLxdxf|fxepipdqipas`"
3479       "pkpcp  ZqHqGplpdt_pmplpmshsMqZqaplplp]q&q^qiqaq[qat`plqbvcx_q`ucrkr:uc{cucq`qlvlqjqlqcwdq\\qgqgxdvcqj"
3480       "tfyeq]q]s_q]qmsmqgqmqmqfqfqgwdqfqgwcv_q`qiqdqlqbqmqmqmqfr`s]qbq\\q[q#pjqcrlrdqkpcrlrcqkrdq^rlrcrlrdq]"
3481       "q]qmqaq]rlrlqirlrdqkqcrlrerlrcr_qjpbq]qjqdqkqcqlslqhqmqbqkq^q_q`qZq_tjpSqmsmpgrlsdqnsaqmqbqkqdq\\rlrd"
3482       "qlq_raqjqeqgqgrnqnrdqlqcqmqmqcqkqerkq`qaycqlq_rbq^qfqhqlqlqfqhqgqlqlqgqnvnqgrYqYrbqbrirbqLq_rnpmpdwa"
3483       "qmqcydq^qlqLpmpfpkpkq`plpa{RpltkpB{gpXpLqjqdtmpcqHp]plp_plp`pipjpipipmsfplpjphr_qlqbqlqbqlqbqlqbqlqb"
3484       "qlqbqlxkq\\xdxdxdxdq]q]q]q_vjqgqmqmqfqfqhqfqhqfqhqfqhqfqdrnrfqkqlqgqiqeqiqeqiqeqiqcsaqjqdqnq`pjqcpjqc"
3485       "pjqcpjqcpjqcpjqcpjrlrjqkpbqkrdqkrdqkrdqkrdq]q]q]q]qkrdrlrdqkqcqkqcqkqcqkqcqkqOqkqcqjqdqjqdqjqdqjqdqk"
3486       "qcrlrdqkq:pnwnpgpnwnpgplslpgpkrlpgpkqkpfq^qlq6qaqlpMzfzfzfzgqipdqipbqmp`qmqc|  fqHqHqlpcuasmplpmpiul"
3487       "qSqZq]p^{+q^qiqaq\\q`ubqlqbpkrdrkrarawcx<tEteq`qlqlqlqjqlqcwdq\\qgqgxdvcqjtfyeq]q]t`q]qmsmqgqmqmqfqfqg"
3488       "vcqfqgv_t`q`qiqdqlqbqmqmqmqgs_q]qaq\\q[q\"vcqjqeq]qjqdqiqdq^qjqcqjqdq]q]qnq`q]qkqkqiqjqeqiqdqjqeqjqcq^"
3489       "s^q]qjqdqkqbqmsmqgqmqbqkq_qas_qYsc{Spkqkphqkrcqntcvcqiqeq\\qjqdqmr`tbqjqeqgqgqmqmqdqlqcqmqmqdqiqfqiqa"
3490       "qaycqlq_qaq^qfqhqlqlqfqhqfqmqmqfqnvnqh}cqc}cqc}cqLq_qmpawbqkqasaq^qkqMpmpfpjsnpaplp`{RplpmqkpB{huatK"
3491       "qjqbrmpcqJt^r]plpctlpjqktlpmpkpltlpjqhq^qlqbqlqbqlqbqlqbqlqcrlrcqlxkq\\xdxdxdxdq]q]q]q_vjqgqmqmqfqfqh"
3492       "qfqhqfqhqfqhqfqcteqlqkqgqiqeqiqeqiqeqiqbq`qkrdqmravbvbvbvbvbvbvjqkq]qiqeqiqeqiqeqiqdq]q]q]q^qiqdqjqe"
3493       "qiqeqiqeqiqeqiqeqiqd{hqkpnqdqjqdqjqdqjqdqjqdqkqcqjqdqkq:pnwnpgpnwnpgplslpgplrkpgpkqkpfq^qlq6qaqmqMzg"
3494       "|fxdxfqipdqipbqmqaqmqcp  \\wLqK{dt]qmqmqkrmrnrSqZqK{TtKq^qiqaq]r\\rdqkq\\qdqiqaqarkrcsmq<tEtfq_qlqlqlqk"
3495       "qjqdqjqeq\\qgqgq]q^qgqfqiqeq]q]qnraq]qmsmqgqlqnqfqfqgq^qfqgqkq]raq`qiqdqlqbqnqkqnqgt`q^raq\\q[q#wcqjqe"
3496       "q]qjqdydq^qjqcqjqdq]q]s_q]qkqkqiqjqeqiqdqjqeqjqcq]uaq]qjqcqmqaqmpmpmqfs`qmq_ras_qYscpjtRpkqkphqkrcqk"
3497       "reqlrcqiqcr_qjqdqmq_qnqbqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaqaqiqdqjqaq`q^qfqhqlqlqfqhqfrnqnrfqfqh}cqc"
3498       "}cqc}cqLq_qmp_q^qkq`qMrlqMpmpfpWplpUqRplplqlp=q&qjq`pmp _plp]qkpnpdqhpeqkpnpiq^qjqdqjqdqjqdqjqdqjqdq"
3499       "jqdqkqdq\\q]q]q]q]q]q]q]q]qgqgqlqnqfqfqhqfqhqfqhqfqhqfqbrdqmqjqgqiqeqiqeqiqeqiqbq`wcqlrcwcwcwcwcwcwc~"
3500       "kq]yeyeyeydq]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqd{hqlpmqdqjqdqjqdqjqdqjqcqmqbqjqcqmq9pkqkpgpepgpmumpgp"
3501       "mrjpgpepfq]pmq:{epmpLzg|evbveqipdqipbqmqaqmpbq  [qHqK{cpmq^plqmqkqktRqZqFqOtKq^qiqaq^rZqdy^qdqiqaqaq"
3502       "iq]q:uc{cudq_qlqlqmqjxdqiqfq\\qgqgq]q^qgqfqiqeq]q]qmrbq]qlqlqgqlqnqfqfqgq^qfqgqkr]qaq`qiqcqnqaqnqkqnq"
3503       "hrnq`q_r`q\\q[q$qjqcqjqeq]qjqdydq^qjqcqjqdq]q]s_q]qkqkqiqjqeqiqdqjqeqjqcqZsbq]qjqcqmqaqnqmqnqfs`qmq`r"
3504       "^r`qZr9pkqkphqkrcqjqeqkqcqiqet_qjqcqnq`rnqbqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaqaqiqdqjqbr`q]qhqgrmqmr"
3505       "fqhqeweqfqgrYqYrdpnqnpdrirdpnqnpNq_qmp_q]qmqcyPrmqMqmpkpmqkvaplpVqRqmpkpmq=q&qjq`pmp(v_plp\\pkpmpdphq"
3506       "epkpmpjq]xdxdxdxdxdxdwdq\\q]q]q]q]q]q]q]q]qgqgqlqnqfqfqhqfqhqfqhqfqhqfqcteqnqiqgqiqeqiqeqiqeqiqbq`vbq"
3507       "jqeqjqdqjqdqjqdqjqdqjqdqjqdqjxkq]yeyeyeydq]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqQqmplqdqjqdqjqdqjqdqjqcq"
3508       "mqbqjqcqmq9qlqlqgqgqgqnqmqnqgqnqjqgqgqfq]qnq:{eqnpLzg|dt`tdqipcpipbpkp`sbq  Zq plq`pmq_pkqmqkqjrQqZq"
3509       "Fq'q]rkraq_rYqdy^qdqiqbq`qiq^q6uf{fuaq_qlyjzeqiqeq]qhqfq]q]qhqfqiqeq]q]qlrcq]qlqlqgqkseqhqfq]qhqfqjq"
3510       "]qaq`qiqcqnq`skshrmraq_q_q[q\\q$qjqcqjqeq]qjqdq\\q^qjqcqjqdq]q]qnq`q]qkqkqiqjqeqiqdqjqeqjqcqXqbq]qjqcq"
3511       "mqaqnqmqnqgqmq`s_q\\q`qZq7pmpnqmpgqkrcqjqeqkpbqiqeq\\qjqcs_qlqcqjqeqlqlqgqmqmqdqlqcqmqmqdqiqfqiqaq`qkq"
3512       "drjrdr_q]riqfrnqnreqhqducqhqerZqZrdwdrkrdwOq_qmp_q^w`q`q[sKplslpTplpWqQpmpkqnp<q&qjq`pmp aplp\\pkplpe"
3513       "phqepkplpjq^zfzfzfzfzfzfxcq]q]q]q]q]q]q]q]q]qhqfqkseqhqfqhqfqhqfqhqfqhqcrnreriqfqiqeqiqeqiqeqiqbq`q]"
3514       "qjqeqjqdqjqdqjqdqjqdqjqdqjqdqjqdq]q]q]q]q\\q]q]q]q^qiqdqjqeqiqeqiqeqiqeqiqeqiqQqnpkqdqjqdqjqdqjqdqjqb"
3515       "saqjqbs7qmqmqeqiqeqiqeqiqeqiqeq]qnp7q]rJrnpnresnpnsct_rcqipcqkqcqkqasaq  [rkp&plpcplpnr`qkqmqkrltRqZ"
3516       "qFq'q\\qkq`q`r_pjr^qcpjrcqkrbq`rkrdpkr3sSsLrlrnrhqhqeqjreripeqjsfq]q]riqfqiqeq]q]qkrdq]qgqgqkserjrfq]"
3517       "rjrfqjrfpiraq_qkqbt`skshqkqaq`q^q[q\\q$qkrcrlrdqkpcrlrcqipdq^rlrcqjqdq]q]qmqaq]qkqkqiqjqdqkqcrlrerlrc"
3518       "q^pjqbq]rlrbs_rkrfqmq`s`r\\q`qZq6qlrfrmscrlrepkqbrkqdqkpaqjqcs`rlqcrlrernsnrgrnqnrdqlqcrnqnrdrkqdqkra"
3519       "q`qkqdqhqer^q\\rkqdwdqhqbqarjrdpYqYpbubpipbuNq_rnpmpbq^qnqnq`q`qZqIpgpRplp7pgp;q&rlr`pmp bplp[pkufpiq"
3520       "dpkukrlpcqhqfqhqfqhqfqhqfqhqfqhqfqjqcripeq]q]q]q]q]q]q]q]qjsfqkserjrfrjrfrjrfrjrfrjrdrlrfrjreqkqcqkq"
3521       "cqkqcqkqaq`q]qnplqeqkrdqkrdqkrdqkrdqkrdqkrdqksjpjqkpbqipdqipdqipdqipdq]q]q]q]qkqcqjqdqkqcqkqcqkqcqkq"
3522       "cqkq^qbqkqcrlrdrlrdrlrdrlrbsarlrbs6qkqcqkqcqkqcqkqcqkqdq\\r7q\\qFp\\p]r^rcqipcvbqkqas`r  \\vOqIqlpcw_pip"
3523       "mpivnrRpZpEqbqIq^q[ubwdxdw]qcwbwaq_wcvbq]qRpSp[q^q^qhqexcxeyexdq\\xeqiqeq]q]qjrexdqgqgqjrdxeq\\xeqiqfx"
3524       "`q_war_ririqiqbqazfq[q\\q$xcwcvbwcxdq]wcqjqdq]q]qlqbq]qkqkqiqjqdwcwcwcq^wbu`wbs_rkrgqkq`q`w`q`qZq$yew"
3525       "dqmq`wdvaqjqbr`qkqcyeyewcqlsdwcxdw`sauczexdq^umteucqhqbq`xLqJsKsMq^vdxdpgpaq`qYqIqkq bqkq?{+yapmp Jp"
3526       "fpfpipcpfpiucqhqfqhqfqhqfqhqfqhqfqhqfqjxixexdxdxdxdq]q]q]q]yeqjrdxdxdxdxdxdrjrgpnwdwcwcwcwaq`q]qnuex"
3527       "dxdxdxdxdxdvnwjvbxdxdxdxdq]q]q]q]wcqjqdwcwcwcwcw^qbwbwcwcwcwaq`w`q4uauauauaucq\\r7p[qFp\\p\\p\\pbqipasap"
3528       "ip`q^y  ctNqIqmqbu_phsgslrSq\\qEqbqIq^qZsawdxcu\\qbt^taq]uataq]q q]qgpiqfqfw`udwcxdqZudqiqeq]q]qirfxdq"
3529       "gqgqjrbtcqZtcqirfv_q]s_r_rirjrircqazfq[q\\q#tnqcqns`s`snqaucq\\snqcqjqdq]q]qkqcq]qkqkqiqjqbsaqnsasnqcq"
3530       "]t_t_snqaq^rkrhrkraq`w`q`qZq#smrevbs^t`s`qjqbq`qiqdqnrmqdrmrcubqkrcubqntat^r`sc|fxdq^umtcqaqhqbq^tJq"
3531       "IqIqLq]tcxLq`qYqHu `u>{+qnrmqapmp Kpepgpiuhpephscqfqhqfqhqfqhqfqhqfqhqfqhqixgudxdxdxdxdq]q]q]q]wcqjr"
3532       "bt`t`t`t`taphpgplt`s_s_s_s_q`q]qmsctnqctnqctnqctnqctnqctnqbsktgs_uauauaucq]q]q]q[saqjqbs_s_s_s_sNpms"
3533       "_snqbsnqbsnqbsnqaq`qns_q !p Zp      jp#q\\q6q7q   lq [sjq  Qq -q  OqZq]q  Cq;q HqWq $rIq`qZq _q iqbqK"
3534       "qFqIq`q     hp$q]u   JqYpmpLp   .p        jp    ]p Xr`q[r !p       Tp\"p\\p6q6q   mq Yx  Qr -r  Ps\\q_s"
3535       "  Ipkq:q HqWq $qHq`qZq _q iqbqKqFqIq`q     hp$q]t   IqYpmpLq   /q        kq     Fq_q[q #s       Tp\"q"
3536       "^q6p   1p Vu  Rs    YsJsMy &v<s HqWq &sHtcq]t _q iqbqKqFqIq`q     hp$q   2q2q   /q        kq     Hs_"
3537       "q]s \"q                (r     Xy %t;r GqWq &rFscq]s ^q iqbqKqFqIq`q         ,q4r   0r        lr     G"
3538       "r^q                               *q                                                                "
3539       "                   kr               i";
3540 
3541     // Define a 47x53 font (extra-large size).
3542     static const char *const data_font47x53 =
3543       "                                                                                          "
3544       "                                                                    9])]2_2]T\\8^U^3]  E])]"
3545       "2`4^U^>])]2_4^U^ 6^T\\5])]1_2]T\\8^U^  K])]2`4^V^3]                                         "
3546       "                                                                                          "
3547       "                                                        U]*\\2a4`V\\8^U^5a  F]*\\1\\X\\4^U^=]*\\"
3548       "2a5^U^ 7aV\\4]*\\1a4`V\\8^U^  J]*\\1\\X\\4^V^3\\                                                 "
3549       "                                                                                          "
3550       "                                                S],\\1\\W\\5g8^U^6c  F],\\1\\V\\5^U^<],\\2]W]6^U^"
3551       " 8h3],\\0\\W\\5g8^U^  I],\\1\\V\\5^V^4\\      ;]                                                 "
3552       "                                                                                          "
3553       "                                         :\\-]2\\U\\6\\V`7^U^7]U]  F\\-]2\\T\\6^U^;\\-]3]U]7^U^ 8\\"
3554       "Va1\\-]1\\U\\6\\V`7^U^  H\\-]2\\T\\6^V^5]      =a                                J]              "
3555       "                                                                                          "
3556       "                                            N\\/]2\\S\\7\\T]6^U^7\\S\\  E\\/]2\\R\\7^U^:\\/]3]S]8^U^"
3557       " 8\\T^/\\/]1\\S\\7\\T]6^U^  G\\/]2\\R\\7^V^6]      =c                                L^           "
3558       "                                                         *^                            U` "
3559       "                                        O^             )\\S\\                     !^$^3\\  E]"
3560       "U\\  K^$^4^ G^$^4]   J^$^3\\   #^$^3\\ 4^            B[                                      "
3561       "                              &^                            Xe                            "
3562       "             S^             (\\S\\               )Z      Q^&^3^2]S\\ A\\S\\  K^&^3^ F^&^4_  >]S"
3563       "\\9^&^3^2]S\\   W^&^3^ 6^        Q]    M[               ?`   ![1^H]?` =]4](\\    %` >b4c  Bb "
3564       "?`2a    .a   Ib   Pb      Aa <a @b      Fb =b  F^ :] '] Da A].].].].]            <_:]._   "
3565       " Xh ?c   W^       @`   La   Pa        Sa   Va5^U^ @`   \"f4_ >`0`*^   $^.` <^F]F^F]G`G]    "
3566       " F\\S\\ ;b        %a2a2a2a2a <bR\\     D`4^(^3`4`U\\8^V^6\\S\\  J^(^3`4^U^@^(^3_4^U^/^/`U\\8^(^3`"
3567       "4`U\\8^V^  K^(^3`4^V^1^9]+^V^      ?`    O\\  D\\6]M]            We D]1]T] 9[3bJ\\@e<])]2])\\  "
3568       "  T]0d3_7h9i/_;k5f?n:f7e    3g :_8i3h@h9n?l5iB]H]C].].]J^B].`I`H_J]<g?g1g?g4hAuB]H]G]C]F]K"
3569       "_K]S^J^F^G^CrBb7]*b'_ D] :] '] Fc A].].].].]            >a:].a   !^T_ Bg   `       Dd2_8n?"
3570       "m7g3]:rD]P]P]@g <] 8] 8] B] 3e J^K^ If7^U^+b@d   Fb@f5a Ad4e-] :f  Ra0d AaF\\HaF\\HeJ\\?]._0_"
3571       "0_0_0_2\\U\\0tHh@n?n?n?n?].].].]-h:_J]<g8g8g8g8g BhV]G]H]C]H]C]H]C]H]G^G^B]*d5](]2\\X\\4aW]8^V"
3572       "^6\\S\\  I](]3]X]5^U^?](]3\\W\\5^U^.^R[9aW]7](]2\\X\\4aW]8^V^  J](]2\\X\\4^V^1]8]+^V^      ?a>w   "
3573       "P[ 9[/a:aQa7[    Wl      \"h E]1]T]+\\R\\;[4dL]Ag=])]2])\\    U^1f8c8k;j1`;k7h?n;h9g    5i*b:_"
3574       "8k6kBl=n?l7mD]H]C].].]L_A].`I`H`K]>kAj6kAj9kBuB]H]F]E]E^L_L^R^L^D^I^BrBb7^+b(a D] ;] '] Gd"
3575       " A].].].].]      ;]     (b:].b   #^Q] Dj  !a       Ff3_8n?m8i4]:rD]P]P]Bk ?_ 9] 9_ C]&[0f "
3576       "I]K]=]0g7^U^-fC\\S]   IfBf6c B[S]5[S].] <i  R\\W\\1]T] B\\W\\G]H\\W\\G]H[S]K]?]._0_0_0_0_2c1uIkBn"
3577       "?n?n?n?].].].]-l>`K]>k<k<k<k<k EoF]H]C]H]C]H]C]H]F^I^A],h6]*]2\\V\\6]Wa7^V^6\\S\\  H]*]2\\V]6^U"
3578       "^>]*]3]W]6^U^._V_;]Wa5]*]2\\V\\6]Wa7^V^  I]*]2\\V\\5^V^2]7]+^V^      @]W\\=v   P[ 9\\1c<cSd:]   "
3579       "\"o      #_S^ F]1]T],]S];[5^V^N]A_T]=]*]0]*\\    U]1^T^;e8`S_<^R_2`;k8^R]?n<_T_;^S^    6^S_."
3580       "i>_8m:`R`Cn?n?l9`QaE]H]C].].]M_@].aKaH`K]?`S`Bk8`S`Bk;_R_BuB]H]F]E]D]MaM]P]L]B^K^ArB]1]&])"
3581       "c D] <] '] G] :].].].].]      ;]     (^6]*^   #]P^ E^P\\   V^       H^T^4_8n?m:`S`6]:rD]P]P"
3582       "]C`S` Aa :] :a D]&[1^S\\ I^M^=]0^R[7^U^/^R^EZO\\   L^R^ N]U] :],\\0] <j  M\\2]R] >\\H]B\\H]=\\M]>"
3583       "]._0_0_0_0_0_/uK`R`Cn?n?n?n?].].].]-n@`K]?`S`>`S`>`S`>`S`>`S` H`ScE]H]C]H]C]H]C]H]E^K^@],^"
3584       "T^5],]1\\V\\6\\U`7^V^6]U\\  F],]2\\T\\6^U^=],]2\\U\\6^U^-e9\\U`4],]1\\V\\6\\U`7^V^  H],]1\\V\\5^V^3]6]+^"
3585       "V^  B`1`1`1`1`6]W]>u   P[ 9]2e>eUf;^   %q      $^O\\ F]1]T],]S];[5]T]N\\@]P[=]*]0]2ZR\\RZ   $"
3586       "]2]P]<_W]8]N]<ZL^4a;]+]MZ/]<^P^=^Q^    7\\O]1nAa9]N_<_M]C]NaA].]+_L^E]H]C].].]N_?].aKaHaL]@"
3587       "^M^C]P_:^M^C]P_=^M\\6]6]H]F^G^D]MaM]P^N^B^K^-^B]1]&]*e D] =] '] H] 9].].].].]      ;]     )"
3588       "^5])^   %^O]8^3]LZ   U]       I^R^6a9_0]+^M^7]:]H]D]P]P]D^M^ Cc ;] ;c E]&[2^PZ H]M]<]1^-^U"
3589       "^1]L];[   N]L] Q]S] :\\,\\1] <dU\\  M\\2\\P\\ >\\H\\A\\H\\<\\M\\=]/a2a2a2a2a1_/]V];_M]C].].].].].].].]"
3590       "-]ObBaL]@^M^@^M^@^M^@^M^@^M^ J^N`D]H]C]H]C]H]C]H]E^K^@]-^Q]5].]1\\T\\7\\S]6^V^5c  E].]2]S\\7^U"
3591       "^<].]2\\S\\7^U^,a6\\S]2].]1\\T\\7\\S]6^V^  G].]1\\T\\6^V^4]5]+^V^  De6e6e6e6e9\\U\\>u   P[ :_3f@gVf<"
3592       "_   &r      $]M[ F]1]T],\\R]>d<^T^P]A^OZ=]+].]4]T\\T]   &^3^P^=[S]8[K].]4\\X];],]!]<]N]>^O^  "
3593       "  8ZM^3`P`Ba9]M^=^J\\C]K_B].],^H\\E]H]C].].]O_>].aKaHaL]A^K^D]N^<^K^D]N^>]JZ6]6]H]E]G]C]MaM]"
3594       "O^P^@^M^-^A]1]&]+_W_ D] >] '] H] 9]  B].]      ;]     )]4](]   %]N]:c6]   G]       J^P^7a8"
3595       "_1],^K^;c=]H]D]P]P]E^K^ Ee <] <e F]&[2] =^O^<]1] 0\\H\\<\\   P\\H\\ R\\Q\\+]3\\,\\2] <eU\\  M\\3]P\\ >"
3596       "\\I]A\\I]<\\N]=]/a2a2a2a2a2a1]U]<^J\\C].].].].].].].]-]K_CaL]A^K^B^K^B^K^B^K^B^K^ K]K^D]H]C]H]"
3597       "C]H]C]H]D^M^?]-]P]4]0]1\\R\\  Ha  C]0]2]R] E]0]2\\Q\\ 9c 9]0]1\\R\\   !]0]1\\R\\ ?]4]   Di:i:i:i:i"
3598       ";\\6]G]   P\\ :`5g@gWh>a   (_       J]KZ F]1]T],\\R\\?h>]R]P\\@]1]+].]3^V\\V^.]   T]2]N]5]8ZJ]-]"
3599       "6]X];]-]!^=]L]?]M]    *]5_J_Ec:]L^>]H[C]I^C].],]F[E]H]C].].]P_=].]X]M]X]HbM]A]I]D]M]<]I]D]"
3600       "M]?]%]6]H]E]G]C^NaN^N]Q^>^O^-^@]0]'],_U_  &] '] H] 9]  B].]      ;]     )]4](]   %]N]:d7] "
3601       "  F]       K]N]8c8^1],]I]>i@]H]D]P]P]E]I] Fg =] =g G]&[2] <]O];]1] 1\\F\\=\\   Q\\F\\ S\\Q\\+]3\\."
3602       "]  IeU\\  M\\3\\N\\ ?\\I\\@\\I\\=]M\\<]0c4c4c4c4c3a1]U]<]H[C].].].].].].].]-]J_DbM]A]I]B]I]B]I]B]I]"
3603       "B]I] L]J_E]H]C]H]C]H]C]H]C^O^>].]N]    .]        '`X_           I]   FbWa=bWa=bWa=bWa=bWa<"
3604       "\\6^I^  ?Z2[ :a5gAiXh?c   *^       H] 7]1]T]-]S]Aj>]R]Q]@]1],],\\1^X\\X^,]   T]3]L]6]'].]7]W]"
3605       ";]-]!]<]L]?]M^    +]6^F^F]W]:]K]?]FZC]H^D].]-]DZE]H]C].].]Q_<].]X]M]X]H]X]M]B]G]E]M^>]G]E]"
3606       "M^@]%]6]H]E^I^B]O^X]O]M^R^=]O^-^@]0]']-_S_  '] '] H] 9]  B].]      ;]     )]4](]   %]N]:e8"
3607       "_   H]       L]M]8]W]7^2]-]G]AmB]H]D]P]P]F]G] Hi >] >i  J[3] ;^Q^;]1] 2\\RbT\\Ge   R\\VdR\\ T\\"
3608       "Q\\+]4\\2a  IfU\\  M\\3\\N\\ ?\\J\\?\\J\\AaM\\ G]W]4]W]4]W]4]W]4]W]4c3^U]=]FZC].].].].].].].]-]H]D]X]"
3609       "M]B]G]D]G]D]G]D]G]D]G]A[H[B]J`E]H]C]H]C]H]C]H]B]O^>g8]N]             1]T_      3[    9]   "
3610       "G_O^?_O^?_O^?_O^?_O^=\\5]I^  @\\3[ ;c6gAy?d7`8]L]7^7]L]>^       H] 6]1]T]-]S]B_W[U]>]R]R]?]1"
3611       "],],]0d*]   T]3]L]6]'].]7\\V];].] ]<]L]@]K]  7Z PZ X]7^D^G]W]:]K]?]/]G]D].]-]/]H]C].].]R_;]"
3612       ".]X^O^X]H]X^N]B]G]E]L]>]G]E]L]@]%]6]H]D]I]A]O]W]O]L^T^<^Q^-^?]0]'].^O^  Sb7]U`2b4`U]8a8])`"
3613       "7]T_  M].]%_O_@_2`0`3`/_3c9]     )]4](]   N_6]N]3^7a/c0_ <^  D[U^  Ga  N]L]9]W]6^3]-]G]B`W"
3614       "]W`C]H]D]P]P]F]G] I_X]X_ ?] ?_X]X_  Nb7]2ZFZ=]Q]:]0] 3[SfU[Ig   R[UfS[ T\\Q\\+]5]2a  IfU\\  M"
3615       "\\3\\N\\ ?\\K]?\\K]AaN] G]W]4]W]4]W]4]W]4]W]4]W]3]T]=]/].].].].].].].]-]G]E]X^N]B]G]D]G]D]G]D]G"
3616       "]D]G]B]J]C]KbF]H]C]H]C]H]C]H]B^Q^=j;]P_9b3b3b3b3b3b3bN`Bb3a2a2a2a    V_2_2`1`1`1`1` ;aU]  "
3617       "  :]U`   S^T]U^A^L^A^L^A^L^A^L^?]5]I]  @^5\\ <e7gAy@f;e:]L]8`8^N^?^       G] 6]1]T]-\\R\\A]U["
3618       "RZ>]R]R\\>]1],],].`(]   U^3]L]6]'].]8]V];].]!^<]L]@]K]  :] P]#^8^A]I^W^;]K]@].]G^E].].].]H]"
3619       "C].].]S_:].]W]O]W]H]W]N]C]E]F]L]?]E]F]L]@]%]6]H]D]J^A]O]W]O]L^U^:^S^-^>]0^(]/^M^  Wh:]Wd6f"
3620       "8dW]:e>h2dW]?]Vd<].].]O_>].]WdScK]Vd8f;]Wd7dW]?]Wa6h>h6]L]B]I]A]P`P]K^L^B^K^@l4]4](]   PdU"
3621       "]A]N]2^8e5g;]Vd?^J^8]6]L] E]V`>pA]S]S]:e6kDo>]L]:^W^6^4].]E]D_U]U_D]H]D]P]P]G]E] K_W]W_ @]"
3622       " @_W]W_  Qf9]3\\H\\>^S^:]0_ 6[ThT[K]Q\\   S[T\\R]S[ U]S]+]6],] ?]L]@fU\\  M\\3\\N\\ ?\\K\\>\\K\\;]O\\ G"
3623       "^W^6^W^6^W^6^W^6^W^5]W]4^T]>].].].].].].].].]-]G^F]W]N]C]E]F]E]F]E]F]E]F]E]D_L_E]K]W]F]H]C"
3624       "]H]C]H]C]H]A^S^<k<]Ra<h9h9h9h9h9h9hTeFf7e6e6e6e;].].].]\"^;]Vd8f7f7f7f7f/^6eX]@]L]?]L]?]L]?"
3625       "]L]B^K^?]Wd>^K^  O]S]S]B]I]B]I]B]I]B]I]@]5^K^  @]4[ ;f8gAyAg<h<]L]8`7]N]>]       F] 6]1]T]"
3626       "-\\R\\B]T[6]R]S]>^2]-]*\\.`(]   U]2]L]6]'].]9]U];].]!];]L]@]K]  =` P`'^7]?\\I]U];]K]@].]F]E].]"
3627       ".].]H]C].].]T_9].]W]O]W]H]W^O]C]E]F]L]?]E]F]L]@]%]6]H]C]K]@^P]W]P^K^V^9]S]-^=]/](]0^K^  Xi"
3628       ";]Xf9h9fX]<h?h3fX]?]Xg=].].]P_=].]XfVfL]Xg:h<]Xf9fX]?]Xb7i>h6]L]A]K]@^Q`Q^J^N^@]K]?l4]4](]"
3629       "   QfW^A]O^1]6f9h;]Xg@_K]7]6]L]=]G]C^Wc@pA]S]S]<h9mDo>]L]:]U]5^5].]E]E^S]S^E]H]D]P]P]G]E]@"
3630       "Z+]V]V^-Z4]5ZKZ:]V]V^  Sh9]4^J^>]S]9]._ 8[U_Q[T[L]P\\   S[T\\Q]T[ T]U]*]7]*] @]L]@fU\\  M\\3\\N"
3631       "\\ ?\\L]>\\L]:]Q]:]1]U]6]U]6]U]6]U]6]U]6^W^5]S]>].].].].].].].].]-]F]F]W^O]C]E]F]E]F]E]F]E]F]"
3632       "E]C_N_D]L^W]F]H]C]H]C]H]C]H]@]S];]P_=]S^8i:i:i:i:i:i:iVgIh9h9h9h9h<].].].]'d<]Xg:h9h9h9h9h"
3633       "0^8k?]L]?]L]?]L]?]L]A]K]>]Xf>]K]  O]R]R]D]G]D]VZOZV]D]KZV]D]G]A]4]K]  @]3[ <g7fAyBi>j=]L]8"
3634       "`7]N]?]       F^ 6]1]T]5uI]T[6]R]S\\<^3]-]*]1d*]   U]3]J]7]'].]9\\T];].\\Ua-^;]L]@]K^?].] Uc "
3635       "Pc+_8]>]J]U];]K]@].]F]E].].].]H]C].].]U_8].]W^Q^W]H]V]O]C]E]F]L]?]E]F]L]@^&]6]H]C]K]?]Q^V]"
3636       "Q]I^X^8^U^.^<]/](]1^I^  ]R_<aT_;_R\\:^Tb=_S^@h4_Ub?bT^=].].]Q_<].aT_X]T^LbT^;_T_=aT_;^Tb?aT"
3637       "Z8_R]>h6]L]A]K]?]Q`Q]H^P^?]K]?l4]4](]   R^U^W]@]O]0^7g;_S];bT^@`L]8_7]L]>]E]E^W]V]@pA]S]S]"
3638       "=_T_<oDo?]K^;]U]5_6].\\D]E]R]R]E]H]D]P]P]G]E]A\\+[U]U\\,\\6]6\\L\\;[U]U\\  S_W[V\\9]3^V`V^=^U^9]/a"
3639       " :[T]G[M\\O\\1ZQZ  M[S\\P\\S[ Ud)]8](\\ @]L]@fU\\  M\\3\\N\\9ZQZ0\\L\\=\\L\\8\\Q\\9]1]U]6]U]6]U]6]U]6]U]6"
3640       "]U]5]S]>].].].].].].].].]-]F]F]V]O]C]E]F]E]F]E]F]E]F]E]B_P_C]L]V^G]H]C]H]C]H]C]H]@^U^;]N^>"
3641       "]T]6]R_;]R_;]R_;]R_;]R_;]R_;]R_X_T^K_R\\:_S^;_S^;_S^;_S^=].].].]*h=bT^;_T_;_T_;_T_;_T_;_T_1"
3642       "^9_T`>]L]?]L]?]L]?]L]A]K]>aT_?]K]  P]Q]R]E]F]E]V\\Q\\W]E]K\\W]E]F]A]4^L]  A^@ZN\\ =i8e@yCk?^R^"
3643       "=]L]9b8]O^?]       Im B]1]T]5uI]T[6]S^T]<^3]-]*]3^X\\X^,]   V^3]J]7](^/]9]T];e7]We/]9]N]?]K"
3644       "^?].] Wd Nd._8]O`U\\T\\K]S]<]L^A]-]F^F].]/]-]H]C].].]V_7].]V]Q]V]H]V^P]D]C]G]L]@]C]G]L]?^']6"
3645       "]H]C^M^?]Q]U]Q]Ic6^W^._<]/^)]2^G^ !ZM^=`Q^=^NZ;^Q`>^P^=].^Q`?`Q^>].].]R_;].`R^X\\R^M`Q^=^P^"
3646       ">`Q^=^Q`?`1]MZ;].]L]A^M^?]Q`Q]G^R^>^M^1^4]4](]  D]P^A]R^X]@]P^/]9^Vb=^NZ;`Q^AaN^8_7]L]>]E]"
3647       "F^V]U]>]P]>]S]S]>^P^>`T`7]6]J]<]S]5^6]/]C]G]Q]Q]F]H]D]P]P]H]C]C^&]TZ,^7]7^N^6]TZ H]/^U[TZ9"
3648       "]2n;]U]8]0d <[U]F[M\\P]2[R[  M[S\\P\\S[ Tb(]9]'\\ @]L]@fU\\  M\\3]P]9[R[1\\M\\<\\M\\7\\R\\8]2]S]8]S]8]"
3649       "S]8]S]8]S]7]U]6]R]?]-].].].].].].].]-]F]F]V^P]D]C]H]C]H]C]H]C]H]C]B_R_C]L]T]G]H]C]H]C]H]C]"
3650       "H]?^W^:]M]>]U^6ZM^<ZM^<ZM^<ZM^<ZM^<ZM^<ZMbP]M^NZ;^P^=^P^=^P^=^P^>].].].]+i=`Q^=^P^=^P^=^P^"
3651       "=^P^=^P^2^:^P^>]L]?]L]?]L]?]L]A^M^>`Q^@^M^  P]Q]Q]F]E]F]W^S^W]F]L^W]F]E]B]3]M^  B^B^O[ =k8"
3652       "d?xClA^P^>]L]9]X]8^P]>\\       Hl A] 9uI]T[5]T]T]:^ =]*]5^V\\V^.]   V]2]J]7](]/^:]S];h:]Xg0]"
3653       "9^P^?]K^?].]!e Je2_7\\PdW\\S\\L]S]<]M^@]-]E]F].]/]-]H]C].].]X_5].]V]Q]V]H]U^Q]D]C]G]L]@]C]G]M"
3654       "^?`)]6]H]B]M]>]Q]U]Q]Hb5c-^;].])]   B]=_O]=].]O_>]N^>].]O_?_O]>].].]S_:]._P`P]M_O]=]N]>_O]"
3655       "=]O_?_1]-].]L]@]M]>]RbR]G^R^=]M]1^3]4](]  FaSaD^Qa?]R_.]9]R`>]._O]>^N]8`7]L]>]E]G^U]U^?]P]"
3656       ">]S]S]>]N]>^P^7]6]J]<]S]4^7]/]C]G]Q]Q]F]H]D]P]P]H]C]D_&]&_8]8_N_7] B]/]T[3]1l:^W^8]1]W` >\\"
3657       "U\\E\\N\\P]3\\S\\  N\\S\\P\\S\\ S_']:]&\\ @]L]@fU\\  M\\2\\P\\8\\S\\2\\N]<\\N]7\\S]8]2]S]8]S]8]S]8]S]8]S]8]S]"
3658       "7]R]?]-].].].].].].].]-]E]G]U^Q]D]C]H]C]H]C]H]C]H]C]A_T_B]M]S]G]H]C]H]C]H]C]H]>c9]M^?]U]']"
3659       ".].].].].].`O^N].]N^>]N^>]N^>]N^?].].].],_R^>_O]=]N]=]N]=]N]=]N]=]N]2^:]O_?]L]?]L]?]L]?]L]"
3660       "@]M]=_O]?]M]  O\\P]Q]F\\D]F\\U^U^V]F\\L^V]F\\D]B]3]M]  RuJ`O[ >m9c>wCmA]N]>]L]9]X]7]P]?]       "
3661       "Im A] 2\\R\\A]T[5^V^T\\:` ?](\\6]T\\T]/]   V]2]J]7])^1_9]S];i;bS^2^8^S_>]K^?].]$e@u@e6_7]QfX\\S\\"
3662       "M^S^=]N^?]-]E]F].]/]-]H]C].].c4].]U]S]U]H]T]Q]D]C]G]M^@]C]G]M]=c-]6]H]B]M]>^R]U]R^G`4c.^:]"
3663       ".])]   B]=^M]?^/]M^?]L]>]/]M^?^N^?].].]T_9].^O_O^N^N^?]M^?^M]?]M^?^0]-].]L]@]M]>^S]X]S^F^T"
3664       "^<^O^2_3]4](]  GcUcE]Pa?]Vb-]:]O_?].^N^>]O^8a8]L]?]C]H]T]T]?]P]>]S]S]?]L]@^N^8]6]J]=^S^4^8"
3665       "]/]C]H^Q]Q^G]H]D]P]P]H]C]E_%]%_9]9_L_8] B]0^T[3]0_T_>cWc=]1]U_ ?[U\\C[N]R^4]T]  N[R\\Q]R[ 'u"
3666       "G]&] @]L]?eU\\  M\\2]R]8]T]3\\N\\;\\N\\7]S\\7]3^S^:^S^:^S^:^S^:^S^9]S]8^R]?]-].].].].].].].]-]E]G"
3667       "]T]Q]D]C]H]C]H]C]H]C]H]C]@_V_A]N]R]G]H]C]H]C]H]C]H]>c9]L]?]U]'].].].].].]._M]O^/]L]?]L]?]L"
3668       "]?]L]?].].].]-^O]>^N^?]M^?]M^?]M^?]M^?]M^ I]O`?]L]?]L]?]L]?]L]@^O^=^M]@^O^  P]P]P\\G]C\\G]T^"
3669       "W^T\\G]M^T\\G]C\\B]3^O^  RuJ[X]P[ >o=\\XaX]BwDoC]L\\>]L]:^X^8]P]?]       E] 5] 3]S]A^U[4dT];b @"
3670       "](]6ZR\\RZ.]   V]2]J]7]*^7d8]R];]R_<aQ^3]5f<^M_?].]'e=u=e:_6\\Q^S`S]N]Q]=l>]-]E]Fm>k=]-rC].]"
3671       ".b3].]U]S]U]H]T^R]D]C]G]M]?]C]G]N^<f1]6]H]B^O^=]S^U^S]F_2a.^9].])]   A]>^M]?].]M^?]L]>]/]M"
3672       "^?^M]?].].]U_8].^N^N]N^M]?]L]?^M]?]M^?^0]-].]L]@^O^=]S]X]S]D^V^:]O]2_2]4](]  H\\U^W]U\\E]Pa?"
3673       "]Vb-];]M^?].^M]>^P]7a8]L]?]C]H]T]T]?]P]>]S]S]?]L]@]L]8]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV"
3674       "]F_$]$_:]:_J_9] B]0]S[3]0]P]>o=]2]S_ @[U\\C[M]T_5^U^;u O[R\\R]Q[ 'uH]/ZQ] ?]L]?eU\\  M\\1]T]7^"
3675       "U^4\\O]O]I\\O]T`MZQ]S]O]E]3]Q]:]Q]:]Q]:]Q]:]Q]:^S^9]QmO]-m>m>m>m>].].].]1hL]G]T^R]D]C]H]C]H]"
3676       "C]H]C]H]C]?_X_@]O]Q]G]H]C]H]C]H]C]H]=a8]L]?]U]&].].].].].].^M]O].]L]?]L]?]L]?]L]?].].].].^"
3677       "M]?^M]?]L]?]L]?]L]?]L]?]L] I]Pa?]L]?]L]?]L]?]L]?]O]<^M]?]O]  O]P]P\\G]C\\G]ScS\\G]N^S\\G]P]P\\B"
3678       "]2]O]  QuF]Q[ >oAqDuDqD]L]?]L]:^X^8^R^?\\       D] 5] 3]S]@`X[3bS\\R^G]W^N] P](].\\&]   W]1]J"
3679       "]7]*^7c8]Q];ZM^=`O^4]4d:]M_?].])d:u:d=_5\\R]O^R\\N]Q]=j<]-]E]Fm>k=]-rC].].a2].]U^U^U]H]S]R]D"
3680       "]C]G]N^?]C]G]P_:g3]6]H]A]O]<]S]S]S]E^1_.^8]-]*]   A]>^M]?]/^M^?]K]?]0^M^?]L]?].].]V_7].]M]"
3681       "M]N]L]@^L]?^M]@^M^?]/]-].]L]?]O]<]S]X]S]C^X^9]O]2^1]4](]0_IZ O[R\\X]S\\G^O_>]Vd9_U];]L]?].]L"
3682       "]=]P]8]X^9]L]?]C]I^T]S]@]P]>]S]S]?]L]@]L^9]6p=]Q]3^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]G_#]#_;];_H"
3683       "_:] B]0]S[3]0\\N\\>o=]2]Q^ A[U\\C[LcX\\6]T]9u O[RfP[ 'uIf7e >]L]>dU\\<] :f5d4]T]:fT\\O^NfT\\UdOeR"
3684       "\\O^F^3]Q]:]Q]:]Q]:]Q]:]Q]:]Q]:^QmO]-m>m>m>m>].].].]1hL]G]S]R]D]C]H]C]H]C]H]C]H]C]>d?]P^Q]G"
3685       "]H]C]H]C]H]C]H]<_7]L]?]U^'].].].].].].^L]P].]K]@]K]@]K]@]K]@].].].].]L]?]L]@^L]@^L]@^L]@^L"
3686       "]@^L] I]Q]X^@]L]?]L]?]L]?]L]?]O]<^M]?]O]  O\\WmX]H\\WmX]H\\QaR]H\\N^R]H\\O]P]C]2]O]  QuF]R\\ ?qC"
3687       "sDtDrE]L]?]L]:]V]7]R]>x      '] 5] 3\\R\\?e3^R\\SbJ^V^O] P](].\\&]   W]1]J]7]+^6e:]Q]-^>_M]5^6"
3688       "h<^O`  Qe8u8e@^5]R\\M]R\\O^Q^>m?]-]E]Fm>k=]KdFrC].].b3].]T]U]T]H]S^S]D]C]G]P_>]C]Gk6f5]6]H]A"
3689       "^Q^<]S]S]S]F_1_/_8]-]*]   A]>]K]A].]K]@]J]?]0]K]?]L]?].].]W_6].]M]M]N]L]@]J]@]K]A]K]?]/^.]"
3690       ".]L]?]O]<]T^W]T]C^X^9^Q^3^1]3]']3dN\\ P\\R`Q[G]N_>]Q`;bW];\\K^?]/]L]=]Q^8]W]9]L]?]C]I]S]S]@]P"
3691       "]>]S]S]@]J]B^L^9]6p>^Q^4^9]/]C]H]P]P]G]H]C]Q]Q]G]ViV]H_\"]\"_<]<_F_;] B]1]R[3]1]N]8a6]2]P^ B"
3692       "[U\\C[K`V\\7]T]8u O[RdN[ 'uIf5a <]L]=cU\\<] :f3`1]T];fU\\N^NfU\\T[S]NaQ\\N^G^3^Q^<^Q^<^Q^<^Q^<^Q"
3693       "^;]Q]:]PmO]-m>m>m>m>].].].]1hL]G]S^S]D]C]H]C]H]C]H]C]H]C]=b>]P]P]G]H]C]H]C]H]C]H]<_7]L]?]U"
3694       "_(].].].].].].]K]Q].]J]A]J]A]J]A]J]@].].].].]L]?]L]@]J]A]J]A]J]A]J]A]J] K]P\\V]@]L]?]L]?]L]"
3695       "?]L]?^Q^<]K]@^Q^  O\\WmX]H\\WmX]H\\P_Q]H\\O^Q]H\\O]P]C]2^Q^  D^<]R[ >qDuEsCqD]L]?]L]:]V]7]R]>x "
3696       "     '] 5] 3\\R\\=f+]TdL^T^P] P](].\\2u  *]1]J]7],^-_=]P],]>_M]5]7_R^<^Qa  Sd .dC^4\\R]M]R\\O]O"
3697       "]>]N_@]-]E]F].]/]KdF]H]C].].]X^4].]T]U]T]H]R]S]D]C]Gk=]C]Gj1c6]6]H]@]Q];^T]S]T^Ga1].^7]-]*"
3698       "]   Lh>]K]A].]K]@]J]?]0]K]?]L]?].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]._0].]L]>]Q];^U]V]U^Bb7]Q]"
3699       "3^1^3]'^6iS^ P[P^P[G]N_>]N^=dX]<]J]>^1]L]=^R]8^W]9]L]@]A]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]>]O"
3700       "]5^8]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]<z=]=z<] B]1]R[7j:\\L\\7_5]2]P^ B[U\\C[ V]T]7u O[R\\U^O[  T] "
3701       "  ]L];aU\\<]   I]T],]O[X\\>]K]@]O[X\\I`3]O]<]O]<]O]<]O]<]O]<]O];]P]?]-].].].].].].].]-]E]G]R]"
3702       "S]D]C]H]C]H]C]H]C]H]C]<`=]Q]O]G]H]C]H]C]H]C]H];]6]L]?]T_4h9h9h9h9h9h9hK]Q].]J]A]J]A]J]A]J]"
3703       "@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]Q\\U]@]L]?]L]?]L]?]L]>]Q];]K]?]Q]  N\\WmX]H\\WmX]H\\P_"
3704       "Q]H\\P^P]H\\O]P]C]1]Q]  C]:]S[ ?sEvEqAoC]L]?]L];^V^8^T^>x      '] 5] 4]S]<g-\\T^V^M]S_Q\\ O](]"
3705       ".\\2u Se =^1]J]7]-^*^?]O],^?^K]7^7]N]<^Sb  Sa (aC]3\\R\\K\\R\\P^O^?]L^A]-]E]F].]/]KdF]H]C].].]W"
3706       "^5].]T^W^T]H]R^T]D]C]Gj<]C]Gj-`7]6]H]@]Q]:]U^S^U]Fb2]/^6]-^+]   Nj>]K]A].]K]@p?]0]K]?]L]?]"
3707       ".].b3].]M]M]N]L]@]J]@]K]A]K]?].c4].]L]>]Q]:]U]V]U]@`6^S^4^5b2]&b<u P[O]P\\H]N^=]M]>^Ua<]J]="
3708       "c7]L]<]S^8]V^:]L]@]A]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?^O^7^7]/]C]H]P]P]G]H]B]R]R]F]C]Iz<]<z="
3709       "]=z<] B]1]R[7j:\\L\\7_ C^P] B[U\\C[ W]T] W] O[R\\T^P[  T]   ]L]7]U\\<]   H]T]-\\O\\X\\>\\I\\@\\O\\X\\J`"
3710       "3^O^>^O^>^O^>^O^>^O^=]O]<^P]?]-].].].].].].].]-]E]G]R^T]D]C]H]C]H]C]H]C]H]C];^<]R]N]G]H]C]"
3711       "H]C]H]C]H];]6]L]?]S`8j;j;j;j;j;j;|Q].pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]R]U]@]"
3712       "L]?]L]?]L]?]L]>^S^;]K]?^S^  N\\WmX]H\\WmX]H\\QaR]H\\Q^O]H\\O]P]C]1^S^  D]9]T\\ ?sFwDo?nC]L]?]L];"
3713       "]T]7]T]=]       Hj ?] 4]S]8d/]T]T]N^R_R\\ O](] =u Se =]0]J]7].^(]?]O]+]?^K]7]7]L]<gX]  Sa ("
3714       "aC]3\\R\\K\\R\\P]M]?]K]A]-]E]F].]/]D]F]H]C].].]V^6].]S]W]S]H]Q]T]D]C]Gg9]C]G]Q_,^7]6]H]@^S^:]U"
3715       "]Q]U]G^X]2]0^5],]+]   Pl>]K]A].]K]@p?]0]K]?]L]?].].a2].]M]M]N]L]@]J]@]K]A]K]?]-f8].]L]>^S^"
3716       ":]U]V]U]?^4]S]4^4`0]$`<^Si O[O\\O\\H]N^=]M^@^S`<]J]=c7]L]<]S]8^U]:]L]@]O]O]J]S]S]@]P]>]S]S]@"
3717       "]J]B]J]9]6]J]?]M]7]6]/^E^H]P]P]G]H]A]S]S]E]C]Iz<]<z=]=z<] B]1]R[7j:\\L\\6] A^Q] B[U\\C[Ni:]T]"
3718       " V] O[R\\S]P[  T]   ]L]6\\U\\<]  Dh2]T]/]P\\W\\?]I\\A]P\\W\\K`2]M]>]M]>]M]>]M]>]M]>^O^=]O]?]-].].]"
3719       ".].].].].]-]E]G]Q]T]D]C]H]C]H]C]H]C]H]C]<`=]S]M]G]H]C]H]C]H]C]H];]6]M^?]R`;l=l=l=l=l=l=~Q]"
3720       ".pApApAp@].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J]?tG]S]T]@]L]?]L]?]L]?]L]=]S]:]K]>]S]  M]P]P\\G]"
3721       "C\\G]ScS\\G]S^N\\G]P]P\\B]0]S]  D]7\\T[ >sFwCn?mB]L]?]L];]T]7]T]=]       Hi >] 4]S]7[Xa1]T^T^O]"
3722       "P_T] O](] =u Se =]0]J]7]/^'^A]N]+]?^K]7]8^L^<eW]  Sd .dC]3\\R\\K\\R\\P]M]?]K]A]-]E]F].]/]D]F]H"
3723       "]C].].]U^7].]ScS]H]Q^U]D]C]G]/]C]G]O^,^8]6]H]?]S]9]U]Q]U]H^W^3]1^4],]+]   Q`P]>]K]A].]K]@p"
3724       "?]0]K]?]L]?].].b3].]M]M]N]L]@]J]@]K]A]K]?]+e9].]L]=]S]9]V]T]V]@_4]S]5_4b2]&b<\\Nd M[O]P\\H]N"
3725       "^=]L]@]Q_<]J]?e7]L];]T]8]T]:]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]?]M]8^6].]E]G]P]Q^G]H]A"
3726       "^T]T^E]C]Iz<]<z=]=z<] B]1]R[3]1\\L\\6] A_R] B\\U\\E\\Ni:]T] V] O\\S\\R]R\\  T]   ]L]6\\U\\<]  Dh2]T]"
3727       "/\\O[V\\?\\H\\A\\O[V\\L`1]M]>]M]>]M]>]M]>]M]>]M]>^O]?]-].].].].].].].]-]E]G]Q^U]D]C]H]C]H]C]H]C]"
3728       "H]C]=b>]T]L]G]H]C]H]C]H]C]H];]6]M]>]Qa>`P]>`P]>`P]>`P]>`P]>`P]>`PoQ].pApApAp@].].].]/]J]@]"
3729       "L]@]J]A]J]A]J]A]J]A]J]?tG]T]S]@]L]?]L]?]L]?]L]=]S]:]K]>]S]  L\\P]P\\F\\C\\F\\T^W^T\\F\\T^M\\F\\C\\B]"
3730       "0]S]  E^7]U[ >sFwBl=kA]L]?]L]<^T^8^V^=]       Ij >] <u=[U^1\\S]R]O]O_U\\ N](] 1] Ge =]0]J]7]"
3731       "0_&]A]N]+]?^K]8^8]J]:aU\\  Pe 4eA]3\\R\\K\\R\\Qo@]J]A].]F^F].].]E]F]H]C].].]T^8].]RaR]H]P]U]C]E"
3732       "]F].]E]F]N^,]8]6]H]?]S]9^V]Q]V^H^V^4]2_4],]+]   Q]M]>]K]A].]K]@],]0]K]?]L]?].].c4].]M]M]N]"
3733       "L]@]J]@]K]A]K]?](d;].]L]=]S]9^W]T]W^@`5^U^5^/_3]'_8ZJ` K[O]P\\H]N^=]L]@]P];]J]@_0]L];]U^9^T"
3734       "^;]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]6]J]@^M^:^5].]E]F]Q]Q]F]H]@^U]U^C]E]G_\"]\"_BZT]TZB_F_;"
3735       "] B]1]R[3]1\\L\\?o I_S] A[U]F[ V]T] W] N[S\\R]R[  S]   ]L]6\\U\\   ']T]/\\O\\V\\@\\H\\A\\O\\V\\M_0o@o@o"
3736       "@o@o?m>l>].].].].].].].].]-]F^G]P]U]C]E]F]E]F]E]F]E]F]E]=d?^V]L]F]H]C]H]C]H]C]H];]6]N^>]O`"
3737       "?]M]>]M]>]M]>]M]>]M]>]M]>]M]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]U]R]@]L]?]L]?"
3738       "]L]?]L]=^U^:]K]>^U^  L\\P]Q]F\\D]F\\U^U^V]F\\U^M]F\\D]B\\/^U^  OuD]V[ =sFwBk;i@]L]?]L]<]R]7]V];]"
3739       "       F^   Nu=[T^3]S]R]O]N_V\\ N](] 1]   ].]L]6]1_%]Aq0]>]K]8]7]J]/]  Md:u:d>]3\\R\\K\\S\\Po@]"
3740       "J]A].]F]E].].]E]F]H]C].].]S^9].]RaR]H]P^V]C]E]F].]E]F]M],]8]6]H]>]U^8]W^Q^W]H^U^4]2^3]+],]"
3741       "   R^M]>]K]A].]K]@],]0]K]?]L]?].].]X_5].]M]M]N]L]@]J]@]K]A]K]?]$`;].]L]=^U^8]W]T]W]@b5]U]5"
3742       "^,]3]']  J\\Q_Q[G]N^=]L]A]O];]J]@].]L];]U]8]R];]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]5]L]?]K];"
3743       "^4].^G^F]Q]Q]F]H]?_W]W_B]E]F_#]#_B\\U]U\\B_H_A\\U]U[ H]1]R[3]1]N]?o H`V] @[T]G[ U]T] X] N[S\\Q"
3744       "]S[  S]   ]L]6\\U\\   (]T]/]P\\U\\A]I]B]P\\U\\M^/o@o@o@o@o@o@m>].].].].].].].].]-]F]F]P^V]C]E]F]"
3745       "E]F]E]F]E]F]E]>_X_?]W^L]F]H]C]H]C]H]C]H];]6]P_=]M^@^M]?^M]?^M]?^M]?^M]?^M]?^M]?].].].].]-]"
3746       ".].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]U\\Q]@]L]?]L]?]L]?]L]<]U]9]K]=]U]  K]Q]Q]F]E]F]W^S^W]F"
3747       "]W^L]F]E]B\\.]U]  NuC\\V[ =eXZXdFgXhAi9h@]L]?]L]<]R]7]V];]       E]   Nu=[S]3\\R]R]O]M_X\\ M]("
3748       "] 1]   ].]L]6]2_$]Aq0]>]K]8]7]J]/]  Ke=u=e<]3\\R\\K\\S\\Po@]J]A].]F]E].].]E]F]H]C].].]R^:].]Ra"
3749       "R]H]O^W]C]E]F].]E]F]M^-]8]6]H]>]U]7]W]O]W]I^S^5]3^2]+],]   R]L]>]K]A].]K]@],]0]K]?]L]?].]."
3750       "]W_6].]M]M]N]L]@]J]@]K]A]K]?]\"_<].]L]<]U]7]W]T]W]Ac5^W^6^+^4](]  H[R\\X]S\\G]N^=]L]A]O];]J]A"
3751       "^.]L]:]W^9^R];]L]@]O]O]J]S]S]@]P]>]S]S]@]J]B]J]9]5]L]?]K];^4]-]G]D]R]R]E]H]>kA]E]E_$]$_B^V"
3752       "]V^B_J_A^V]V] I]1]R[3]0\\N\\>o G`X] ?\\U_Q[T\\ T]T] ] N\\T\\Q]T\\  S]   ]L]6\\U\\   )]T].\\P\\T\\A\\I]A"
3753       "\\P\\T\\N^.o@o@o@o@o@o@m>].].].].].].].].]-]F]F]O^W]C]E]F]E]F]E]F]E]F]E]?_V_@]W]K]F]H]C]H]C]H"
3754       "]C]H];]6k<]L^A]L]?]L]?]L]?]L]?]L]?]L]?]L]?].].].].]-].].].]/]J]@]L]@]J]A]J]A]J]A]J]A]J] K]"
3755       "V\\P]@]L]?]L]?]L]?]L]<^W^9]K]=^W^  J]R]R]D]G]D]W\\Q\\W]D]W\\L]D]G]A\\.^V]  NuC]W[ <cWZXdEfXh@g8"
3756       "g?]L]?]L]=^R^8^X^:]       F]   G\\R\\5[S]4]R]R]O]Lb M](\\ 0]   ].]L]6]3_#]Aq0]>]K]9]6]J]/]  H"
3757       "e@u@e H\\R]M]T]Q^J]A]J]@]/]G^E].]-]F]F]H]C].].]Q^;].]Q_Q]H]N]W]B]G]E]-]G^F]L]-]8]6]I^>^W^7]"
3758       "W]O]W]I^R^6]4^1]+],]   R]M^>^M^@]/^M^?]-]0^M^?]L]?].].]V_7].]M]M]N]L]@^L]?^M^A^M^?] ]<].]L"
3759       "]<]U]7]X]R]X]B^W^5]W]6^)]4](]  H\\T]W]U\\F]O_=]L]A]P^;^L^A]-]L]:]W]8]P]<]L]@]O]O]J^T]T]?]P]>"
3760       "]S]S]@^L]A^L]8]5]L]@^J]=^3]-^I^D^S]S^E]H]<g>]G]C_%]%_A_W]W_A_L_@_W]W_ J]0]S[3]0]P]5]4],b ="
3761       "[ThT[ R]T]!] M[T\\P]U[  R]   ]L]6\\U\\   *]T].]P[S\\B]J]A]P[S\\N].^J]B^J]B^J]B^J]B^J]B^K^A]M]=]"
3762       "/].].].].].].].]-]G^F]N]W]B]G]D]G]D]G]D]G]D]G]?_T_AbK]E]I^C]I^C]I^C]I^;]6j;]K]A]M^?]M^?]M^"
3763       "?]M^?]M^?]M^?]M_?].].].].].].].].]/]J]@]L]@^L]@^L]@^L]@^L]@^L] J^X]Q]?]L]?]L]?]L]?]L];]W]8"
3764       "^M^<]W]  I]R]S]C]H]C]VZOZW]C]VZL]C]H]@\\-]W]  MuC]X[ ;cWZWbDeWZXe>e6e>]L]?]L]=]P]8^X^:]    "
3765       "   F^   H\\R\\5[S]5]Q]R]O^L` K]*] 0]  !^.]L]6]4_\"]2],^>^M]8]6]J]0]  DeCuCe E]R\\M]T\\P]I]A]J]@"
3766       "]/]G]D].]-]F]F]H]C].].]P^<].]Q_Q]H]N^X]B]G]E]-]G]E]L^.]8]5]J]<]W]6^X]O]X^J^Q^6]5^0]+^-]   "
3767       "R]M^>^M]?].]M^?]-]/]M^?]L]?].].]U_8].]M]M]N]L]?]L]?^M]?]M^?] ]<].]M^<^W^6aRbB^V^6]W]7^(]4]"
3768       "(]  GcUcE]P_=]L]A]P]9]L]@]-]L]:^X]9^P]<]M^@]P^O]I]T]T]?]P]>]S]S]@^L]@]L]8]5]M]?]I]>^2],]I]"
3769       "B_U]U_D]H]:c<]G]B_&]&_?_X]X_?_N_>_X]X_ I]0]S[3]0_T_5]4]+` ;[SfU[ P^U^#] L[U\\P]V[  Q]   ]M^"
3770       "6\\U\\   ,^U^-\\P\\S\\B\\J]@\\P\\S\\N].]I]B]I]B]I]B]I]B]I]B]I]B^M]=]/].].].].].].].]-]G]E]N^X]B]G]D"
3771       "]G]D]G]D]G]D]G]@_R_A`J]D]J]A]J]A]J]A]J]:]6g8]K]A]M^?]M^?]M^?]M^?]M^?]M^?]M_?].].].].].].]."
3772       "].].]L]?]L]?]L]?]L]?]L]?]L]?]L]3^;aP]?]M^?]M^?]M^?]M^;]W]8^M];]W]  H]S]T^B]J^B]J^B]J^B]J^@"
3773       "\\-]W]  G^1_ :aW[V`BcW[Wc<d5c=]L]>]N]<]P]7]X]8]       F]KZ   X]S]5[S]5\\P]R]N]K_ K]*] 0]  !]"
3774       ",]N]5]5_\"]1],]<]M]9^6^L^0]  Ad Nd A\\R]O^U\\P^I^B]K^?]H[C]H^D].],]G]F]H]C].].]O^=].]P^Q]H]M]"
3775       "X]A]I]D],]I^E]K]AZH^8]5]J]<]W]5bObJ^O^7]6_0]*]-]   R]M^>^M]?^/]M^?^.]/]M^?]L]?].].]T_9].]M"
3776       "]M]N]L]?]L]?^M]?]M^?] ]<].]M^;]W]5aRaB^U^6c8_(]4](]  FaSaD]P_=]M]@]P]9]L]@]-]L]9b9]O^=^N^?"
3777       "\\P_Q]H]T]T]?]P]=]T]T]?^L]@]L]8]4]N]@^I^?]1],^K^A`W]W`C]H]7]8]I]@^&]&^=i=^N^<i H]0^T[3]1l6]"
3778       "4])_ <\\RbT\\ O]T]#] L\\V\\O]X\\     M^N^6\\U\\   ,]T]-\\OhF\\J]@\\OhQ]/^I^D^I^D^I^D^I^D^I^C]I]B]L]<"
3779       "]H[C].].].].].].].]-]H]D]M]X]A]I]B]I]B]I]B]I]B]I]@_P_B_J]C]J]A]J]A]J]A]J]:]6].]K]A]M^?]M^?"
3780       "]M^?]M^?]M^?]M^?]M_?^/^/^/^/^/].].].].]L]?]L]?]L]?]L]?]L]?]L]?]L]3^;`O]?]M^?]M^?]M^?]M^;c8"
3781       "^M];c  G^U]U^@^M^@^M^@^M^@^M^?\\-c  H^0_ 9^U[U^@aV[Va:b3a<]L]>^P^=^P]7]X]8_       H^M[ F] 6"
3782       "]S]>ZQ[T^6]P]S^N^K^ K]*] 0]:] 8]0],]O^5]6_2ZI]1]-^<^O^9]4]L]0]<].] Uc Pc1]2\\Q^S`W^P]G]B]K]"
3783       ">^J\\C]I^C].],^H]F]H]C].].]N^>].]C]H]MbA^K^D],^K^D]K^B[I]7]5^L^<c5aMaJ^N]7]6^/]*]-]   R^O_>"
3784       "_O]=].]O_>].].]O_?]L]?].].]S_:].]M]M]N]L]>]N]>_O]=]O_?] ]<]-]O_;]X^5aRaC^S^6a8_']4](]  D]P"
3785       "^B^Ra>^N]@]Q]7]N]?^.]L]9a8]N]=^N^?]Q_Q]G]U]U]>]P]=]T]T]?_N]>]N]7]4^P^@]G]@^1]+^M^?mB]H]7]8"
3786       "^K^?\\%]%\\;g;\\L\\:g G]/]T[3]2n7]4]'^ <\\F\\ M\\S\\  J\\F\\     L^N^6\\U\\   ,\\S\\-]OhG]K]@]OhQ]LZ=]G]"
3787       "D]G]D]G]D]G]D]G]D]G]D^L]<^J\\C].].].].].].].]-]J_D]MbA^K^B^K^B^K^B^K^B^K^A_N_B^K]B^L^A^L^A^"
3788       "L^A^L^:]6].]K]A^O_?^O_?^O_?^O_?^O_?^O_?^Oa?].].].].]/].].].]-]N]>]L]>]N]=]N]=]N]=]N]=]N]2^"
3789       ";_O]=]O_>]O_>]O_>]O_:a7_O]9a  E^P_>^P_>^P_>^P_>^P_>\\,a  H^.] /[5]T[S\\8a1`<]L]=^R^<]O^8b7_ "
3790       "      H^O\\ F] 6\\R\\=[R[U^5\\N]T]L^M` L]*] 0]:] 8]1^+]P]4]7_1[L_1]<ZL^:^Q^8]4^N^>ZM];].] R` P"
3791       "`.]2]QfXaN]G]B]L^=^L]C]K_B].]+_J]F]H]C].].]M^?].]C]H]La@^M^C]+^M^C]J]B]L^7]4^N^:a4aMaK^M^8"
3792       "]7^.]*^.]   Q]P`>`Q^=^NZ;^Q`>_LZ>].^Q`?]L]?].].]Q^;].]M]M]N]L]>^P^>`Q^=^Q`?]/ZL];]-^Q`:a4`"
3793       "P`D^Q^7a8^&]4](]   S]Sb>_P^@]R^7^P^>^MZ<]L]9a9]M]=_P`XZB]Q_Q]G^V]V^>]P]=^U]U^?`P^>^P^6]4]Q"
3794       "^?]G]A^0]*^O^<i@]H]7]7^M^=Z$]%Z8e9ZKZ7e F]/^U[TZ9]3^V`V^8]4]&^ <\\H\\ K[R[  I\\H\\     K_P`XZ9"
3795       "\\U\\   ,[R[,\\E\\D\\K]?\\E\\M]O\\=]G]D]G]D]G]D]G]D]G]D]G]D]K];^L]C].].].].].].].]-]K_C]La@^M^@^M^"
3796       "@^M^@^M^@^M^A_L_C`N^A^N^?^N^?^N^?^N^9]6].]L]?]P`>]P`>]P`>]P`>]P`>]P`>]P]X^LZN^NZ;_LZ>_LZ>_"
3797       "LZ>_LZ?].].].]-^P^>]L]>^P^=^P^=^P^=^P^=^P^2^:^P^=^Q`>^Q`>^Q`>^Q`:a7`Q^9a  Dk<k<k<k<k>],a  "
3798       "H]-] /[,[._0_;]L]=j<]N]7`5a       J_S^ F] 6\\R\\=^U[W_5]N^V^K_Rd L],] /]:] 8]1])^T^3]8_0^Q`0"
3799       "]<]Q_8^S^8^3_R_=]R^:].] O] P]+]1\\PdW`N^G^C]N_;`R`C]NaA].]*`O`F]H]C].].]L^@].]C]H]La?`S`B]*"
3800       "`S`B]J]B`Q_6]3_R_9a4aMaL^K^9]8^-])].]   Q_Tb>aS^;_R\\:^Sa=`Q]>]-^Sa?]L]?].].]P^<].]M]M]N]L]"
3801       "=_T_=aS^;^Sa?]/^R_:]-^Sa:a3_P_C^P^7_8^%]4](]   S_V^X^?aS^>]T^5_T_=`R]<]L]8_8]M^>`SdA]SaS]E"
3802       "^W]W^=]P^=_W]W_>]X]T_<_T_5^4^T^?^G^C^/])^Q^8c=]H]7]6`S` ?] ;c >c E]._W[V\\9]4^J^9]4]%] ;]L]"
3803       " IZQZ  H]L] !u  ,`Sd9\\U\\   ,ZQZ,]E\\E]L]?]E\\M_S^>^G^F^G^F^G^F^G^F^G^F^G^F^K]:`R`C].].].].]."
3804       "].].]-]ObB]La?`S`>`S`>`S`>`S`>`S`?]J]CcS`?_R_=_R_=_R_=_R_8]6].]V[R^?_Tb>_Tb>_Tb>_Tb>_Tb>_T"
3805       "b>_T^V_Q]M_R\\:`Q]=`Q]=`Q]=`Q]?].].].],_T_=]L]=_T_;_T_;_T_;_T_;_T_1^:`T_;^Sa=^Sa=^Sa=^Sa9_6"
3806       "aS^7_  Bi:i:i:i:i=]+`  I],] /[,[-].]:]L]<h;]N]7`3q      \"h E] 7]S]=k5]LdIjW^ M],] /]:] 8]1"
3807       "](f9k?n?l/]<j6g7]1j<h9].] LZ PZ(]1]O`U]K]E]Cm8kBn?n?](nE]H]C].].]K^Am>]C]H]K`>kA])kA]J^Cm5"
3808       "]2j7_2`M`K^J]9]8tC])].]   PgX]>]Xf9h9fX]<k>],fX]?]L]?].].]O^=].]M]M]N]L]<h<]Xf9fX]?]/j9d4g"
3809       "X]:a3_P_D^O^7_8m4]4](]   RfXaBk=^V^3h;j<]L]8_9^L]>qA^U]W]U^Di<]O`?k=]Xg:h3a7f>uCn?]/eSe;]:"
3810       "]H]7]5k >] :a <a D]-h>n?\\H\\8]4]%] 9^R^   *^R^  Xu  ,q9\\U\\    /]D\\F]LfH]D\\Li>]E]F]E]F]E]F]E"
3811       "]F]E]F]E]F]JnIkBn?n?n?n?].].].]-n@]K`>k<k<k<k<k=[H[Co<j;j;j;j7]6].]Vf=gX]=gX]=gX]=gX]=gX]="
3812       "gX]=gTjLh9k<k<k<k?].].].]+h<]L]<h9h9h9h9h Fk:gX]=gX]=gX]=gX]9_6]Xf6_  @e6e6e6e6e;]+_  G\\+["
3813       " /].]-[,[9]L];e:^N^8`2p       e D] 7]S]<i4\\JbGgT^ M\\,\\ .]:] 8]1]'d8k?n>i-]<i4e6]0h;g8].]  "
3814       " I]0]3]E]Cl6h@l=n?]&jC]H]C].].]J^Bm>]C]H]K`<g?]'g?]I]Bj3]1h6_2_K_L^I^:]8tC])].]   OdV]>]Wd"
3815       "6f8dW]:i>]+dW]?]L]?].].]N^>].]M]M]N]L];f;]Wd7dW]?]/i7c3dV]9_2_P_E^M^8_8m4]4](]   QdV`B]Xe;"
3816       "d1f8h<]L]8_9]K]>]XdW_@eWeBg;]O`=g;]Vd8f1`6d=uCn?]/eSe;]:]H]7]3g <] 9_ :_ C]+f>n>ZFZ7]4]%] "
3817       "7f   &f  Vu  ,]XdW_9\\U\\    /\\C\\F\\KfH\\C\\Kg=]E]F]E]F]E]F]E]F]E]F]E]F]JnHh@n?n?n?n?].].].]-l>"
3818       "]K`<g8g8g8g8g J]Vh:h9h9h9h6]6].]Ve;dV]<dV]<dV]<dV]<dV]<dV]<eRiJf7i:i:i:i?].].].]*f;]L];f7f"
3819       "7f7f7f F]Xe7dV]<dV]<dV]<dV]9_6]Wd5_  <\\-\\-\\-\\-\\6]+_  FZ*[ /].],Z+Z9]L]8`8]L]7^.m       W` "
3820       "A] 7\\R\\7b2]H^BaP_ O].] .]:\\ 7]2^%`6k?n:b*]9c/a5],b6b5].\\   H]/\\4]C]Di0b=h9n?]#c?]H]C].].]I"
3821       "_Dm>]C]H]J_9a<]$d?]I^?c0].b3_2_K_M^G^;]8tC](]/]   M`T]>]U`2b4`U]7c;])`U]?]L]?].].]M^?].]M]"
3822       "M]N]L]8`8]U`3`U]?],c2a0_T]9_2^N^F^K^8]7m4]4](]   O`R^B]Va8b-`3d:]L]7]9^J]?]V`T]>cUc?c9]N_:"
3823       "a8]T`3`-_4`<wDn?]/eSe;]:]H]7]0a 9] 8] 8] B])b<n @]4]&^ 5b   \"b  Tu  ,]V`T]8\\U\\    0].].]0b"
3824       ";]C]H]C]H]C]H]C]H]C]H^E^H^JnEb=n?n?n?n?].].].]-h:]J_9a2a2a2a2a G\\Rb4b3b3b3b3]6].]Vc7`T]:`T"
3825       "]:`T]:`T]:`T]:`T]:aMcEb2c4c4c4c<].].].]'`8]L]8`1`1`1`1` D]Ua2_T]9_T]9_T]9_T]8]5]U`2]      "
3826       "=]                       &[   O].]  E]  E]         ']    S]        R]      ^       (](]/] "
3827       "       C]  S]    '] V]      F^ 7]4](]   %])[  4]7] @])_Q_:] 9]6]                6[   S]0[R"
3828       "^           H]%\\U\\ A\\            @\\             /Z            <\\             ,[    M^5](^ "
3829       "     =]                       &[   N]0]  D\\  D]         '\\    Q^DZ       1]      _       )"
3830       "](]/]        D^  S]    '] V]      F] 6]4](]   %]   ;]7] @] /] 9]6]                6[   S]0"
3831       "g           H]%\\U\\ @\\            @\\                          J\\                  X]4](]   "
3832       "   <]                       &[   N]0]  D\\  E^         '\\    P^G]       2]      X^       )]"
3833       "(^0]        D]  R]    '] V]      G^ 6]4](]   %]   ;]7] @] /] 9]6]                6[   S]0e"
3834       "           F]%\\U\\ ?[            ?[                          I[                  ^4])^     "
3835       " @ZV]                       &[   M]2]  D]  E]         ']    O_K_       3]      V^       *b"
3836       ",]5b        E^  R]    '] V]      G^ 6^5])^   %]   ;]7] @] /] 9]6]                6[   S].a"
3837       "           D]%\\U\\ ?\\            @\\                          J\\                 !^4])^     "
3838       " B\\V]                       &[   M]2]  D\\            G\\    L`P`       2]      U^       +b "
3839       "=b        RZN^  R^    '] V]      H^ 4^6]*^   $]   ;]7] @] /] 9]6]                6[   S]  "
3840       "          J]  :\\            @\\                          J\\                 \"^3]*^      A\\V"
3841       "\\                       %[   L]4]                   Vm       2^      S^       ,b =b       "
3842       " R\\Q_  R]    &] V]      I^ 3b:].b   $]   ;]7] @] /] 9]6]                6[   S]           "
3843       " J]  @ZU]            FZU]                          PZU]                 #^2]+^      @b    "
3844       "                   %[                       Si       4b                       %i  Ua    &]"
3845       " V]      Mb 2a:].a   #]   ;]7] @] /] 9]6]                   .]            J]  @b          "
3846       "  Fb                          Pb                 'b2]       E`                            "
3847       "                   Qb       1a                       $g  S`    %] V]      Ma /_:]._   !]  "
3848       " ;]7] @] /] 9]6]                   .]            J]  @a            Ea                     "
3849       "     Oa                 &a1]       D^                                                     "
3850       "  X^                 Ip      Fc  Q^    #] V]      M_  A]    )]   ;]7] @] /] 9]6]          "
3851       "                      T]  @`            D`                          N`                 %_/"
3852       "]       BZ                                                                        Ap      "
3853       "                 6]                                                                       "
3854       "                                                                                          "
3855       "                          p                       6]                                      "
3856       "                                                                                          "
3857       "                                                                                          "
3858       "                                                F]']2]    +]']2^ D]']3_   E]']1]   \"]']2^ "
3859       "8]                             H";
3860 
3861     // Define a 90x103 font (huge size).
3862     static const char *const _data_font90x103[] = {
3863       // Defined as an array to avoid MS compiler limit about constant string (65Kb).
3864       // First string:
3865       "                                                                                          "
3866       "                                                                                          "
3867       "                                                                                          "
3868       "                                                                                          "
3869       "            HX     4V         >X       IX           *W             FW                     "
3870       "                                                                                          "
3871       "                                                                                          "
3872       "                                                                                          "
3873       "                                                                                          "
3874       "                                                         HX  W 4Z 3VCT   <Z     >X  W 4Z  "
3875       " HX  W 4Z     'VCT ;X  W 3Y 2UCT       KX  W 3Y   0W                                      "
3876       "                                                                                          "
3877       "                                                                                          "
3878       "                                                                                          "
3879       "                                                                                          "
3880       "                                    @W !W 4\\ 5YET ?XHX 8]     >W !W 4\\ 7XGX KW !W 4\\ 7XHX "
3881       "  +YET :W !W 3[ 5ZFT ?XGX     EW !W 3[ 7XGX 5W                                            "
3882       "                                                                                          "
3883       "                                                                                          "
3884       "                                                                                          "
3885       "                                                                                          "
3886       "                              >W \"V 3\\ 7]HU ?XHX 9`     ?W \"V 3\\ 7XGX JW \"V 3\\ 7XHX   -]HU"
3887       " 9W \"V 3] 7]HT ?XGX     DW \"V 3] 8XGX 5V                                                  "
3888       "                                                                                          "
3889       "                                                                                          "
3890       "                                                                                          "
3891       "                                                                                          "
3892       "                        <W $V 3VNV 8_KV ?XHX 9`     >W $V 3VNV 8XGX IW $V 3VNV 8XHX   -_KV"
3893       " 8W $V 2] 7_KU ?XGX     CW $V 2] 8XGX 6V                                                  "
3894       "                                                                                          "
3895       "                                                                                          "
3896       "                                                                                          "
3897       "                                                                                          "
3898       "                        :W &W 4VLV :j >XHX :VJV     >W &W 4VLV 9XGX HW &W 4VLV 9XHX   .j 6"
3899       "W &W 3VMV 9i >XGX     BW &W 3VMV 9XGX 7W               MW                                 "
3900       "                                                                                          "
3901       "                                                                                          "
3902       "                                                                                          "
3903       "                                                                                          "
3904       "                          CV 'W 4VJV ;j >XHX ;UGV     >V 'W 4VJV :XGX GV 'W 4VJV :XHX   .j"
3905       " 5V 'W 3VKV :i >XGX     AV 'W 3VKV :XGX 8W               N[                               "
3906       "                                                                                          "
3907       "                                                                                          "
3908       "                                                                                          "
3909       "                                                                                          "
3910       "                            DV )W 4VHU <VK_ =XHX ;TEU     =V )W 4VHU :XGX FV )W 4VHU :XHX "
3911       "  /VK_ 3V )W 3VIV <UK_ =XGX     @V )W 3VIV ;XGX 9W               N]                       "
3912       "                                                                                          "
3913       "                                                                                          "
3914       "                                                                                          "
3915       "                                                                                          "
3916       "                                    DV *V 3UFU =UH\\ <XHX <UDT     <V *V 3UFU ;XGX EV *V 3U"
3917       "FU ;XHX   /UH\\ 1V *V 2UGU <TH] =XGX     ?V *V 2UGU ;XGX 9V               a                "
3918       "                                                                                          "
3919       "                                                                                          "
3920       "                                                                                          "
3921       "                                                                                          "
3922       "                                           EV ,V 3UDU >TEY ;XHX <TBT     <V ,V 3UDU <XGX D"
3923       "V ,V 3UDU <XHX   /TEY /V ,V 2UEU =TFZ <XGX     >V ,V 2UEU <XGX :V               Na        "
3924       "                                                                                          "
3925       "                                                                                          "
3926       "                                                                                          "
3927       "                                                                                          "
3928       "                                                   DU -V 3VDV ?TCV :XHX <TBT     ;U -V 3VD"
3929       "V =XGX CU -V 3VDV =XHX   /TCV -U -V 2UCU >TCU :XGX     =U -V 2UCU =XGX ;V               NV"
3930       "IV                                                                          \"W            "
3931       "                                                                                          "
3932       "                                                                                          "
3933       "                                                                                          "
3934       "                                                              JU /V 3VBV     ETBT     :U /"
3935       "V 3VBV   FU /V 3VBV       (U /V 2UAU         DU /V 2UAU   @V               NVGV           "
3936       "                                                               $X                         "
3937       "                                                                                          "
3938       "                                            *X                                            "
3939       "                                                                                          "
3940       "                           JX                                GTBT                         "
3941       "                          MX  GX 7V     :UEU     DX  GX 7V   JX  GX 7W       4X  GX 6V    "
3942       "     GX  GX 5V   (X                            &X                                         "
3943       "                                                                                          "
3944       "                            )X                                                     8V     "
3945       "                                                                                          "
3946       "            ;X                                FTBT                                        "
3947       "           LX  IX 7X     <UCU     DX  IX 7X   JX  IX 6W       3X  IX 6X         GX  IX 5X "
3948       "  *X                            &Y                                                        "
3949       "                                                                                          "
3950       "             (X                                                     9V                    "
3951       "                                                                                       <X "
3952       "                               ETBT                                                   KX  "
3953       "KX 6X 1TBT   BTAT     CX  KX 6Y   JX  KX 6Y     (TBT BX  KX 5X 1TBT       LX  KX 4X   +X  "
3954       "                          %T                                                    #W 9W     "
3955       "                                                                                          "
3956       "3a   :a     <W   2W    >W   E\\   AW ,W ,W ,W ,W                             HY GV +Y      "
3957       "   4Z           NX                 @X                                                     "
3958       "             %W                                DUDU                                       "
3959       "          =Y 7W  KW 6Z 4XDT   BTAT     BW  KW 6Z   IW  KW 6[   ,Y )XDT AW  KW 5Z 4XDT     "
3960       "  KW  KW 4Z   ,W BW                 8V         (S                                         "
3961       "    <S       9V 7V                                                                        "
3962       "                       3a   :a     ;W   3W    >W   H_   AW ,W ,W ,W ,W                    "
3963       "         L] GV +]         ;a          #[                 F^                               "
3964       "            8XGX                      +W                                BTEU              "
3965       "                      *R            9a :W  MW 6\\ 6ZET ?XHX <TAT     AW  MW 6\\ 7XGX LW  MW "
3966       "5[ 7XGX .Y +ZET @W  MW 5\\ 6ZET ?XHX     DW  MW 4\\ 7XHX 0W AW &XHX               MZ        "
3967       " +T                                   $Y         BS 1W,V MY   8W 7W  T           9X   5Z /"
3968       "[     0Z   8Z /Y           GY       .\\       <\\               [   4[   :\\              -a "
3969       "  :a     :W   4W    >W   Ja   AW ,W ,W ,W ,W                             N_ GV +_         "
3970       "?e   8]       J]                 Jb       8[       <[                  $Y       FY 7XGX   "
3971       "=Z         Di 5W   8Z .Y !W         FW *Y   4W)V*W)V-Y(V            <UFU   3\\             "
3972       "       +[ 0[ 0[ 0[ 0[   4[=T            <e ;W  W 5\\ 7\\FT ?XHX <TAT     @W  W 6^ 8XGX KW  W"
3973       " 5] 8XGX .Z@R ?\\FT ?W  W 4\\ 7\\FT ?XHX     CW  W 3\\ 7XHX 1W @W &XHX               N\\       "
3974       "  ,T     :U :U5U                            `   EX 2VFV   .S 4]0W\"b DV  V 5V  T         7W"
3975       " .` 3[ 7c 8d )Z Dq 8b Hy Bb 7`           Na   /Z @k .d Kj ?x Mt 7f MX/X'X -X -X2Z&X -]0]0["
3976       "3X Dc Ii -c Ij 4f N~W$X/X.X&X.X4Y4XDY/Y/Y,Y'~S%a >W $a  MY   EW   5W    >W   Kb   AW ,W ,W"
3977       " ,W ,W                            !a GV +a         Ch   =f       ^                 Mf 2Z @"
3978       "x Mx <c 3X C~Q)X?X?X Kc   2T   .V   .T   CX   $a  !W.W   N` ;XGX ![ Lb       &Z Mi 7[   >a"
3979       " 5a &W   0g    #\\ -_   <\\*V.\\*V0a-V\"X )Z /Z /Z /Z /Z 4WJV 1~U+d Kx Mx Mx Mx MX -X -X -X ,j"
3980       " @[3X Dc 8c 8c 8c 8c   <cBV.X/X'X/X'X/X'X/X/Y,Y$X &h ;W \"W 5VNV 8]HU ?XHX <TAT     ?W \"W 5"
3981       "VNV 8XGX JW \"W 5VMV 9XGX -ZDV @]HU >W \"W 4VNV 8]HU ?XHX     BW \"W 3VNV 8XHX 2W ?W &XHX    "
3982       "           ^ K~\\       >S   3Q +[ @[;[ ;Q                          ;e   HX 2VFV #VBV FS 6`"
3983       "1V#g GV !V 3V !T         7W 0d :` ;j ?k -[ Dq :g Ky Df ;d          $f   1Z @o 5j Np Ex Mt "
3984       ":m\"X/X'X -X -X3Z%X -]0]0\\4X Gi Lm 4i Ln ;m#~W$X/X-X(X-X4Y4XCY1Y-Y.Y&~S%a >W $a  N[   EV   "
3985       "5W    >W   Lc   AW ,W ,W ,W ,W                            \"b GV +a         Dk   Aj      \"_"
3986       "                 h 3Z @x Mx ?i 6X C~Q)X?X?X Ni   6V   /V   /V   DX   &f  #W0W   e >XGX %c#"
3987       "e       +b\"i 9_   Be 9d 'V   3k    %^ /c   @^*V0^*V2d.V\"X )Z /Z /Z /Z /Z 3b 1~U.j Nx Mx Mx"
3988       " Mx MX -X -X -X ,p F\\4X Gi >i >i >i >i   BiEV.X/X'X/X'X/X'X/X.Y.Y#X 'j ;V \"V 5VLV :_IT >XH"
3989       "X <TAT     >V \"V 5VLV 9XGX IV \"V 4VMV 9XGX ,ZHY A_IT <V \"V 4VLV :_IT >XHX     AV \"V 3VLV 9"
3990       "XHX 2V >W &XHX              !_ K~[       >T   4R -_ D_?_ >S         =t                Fh  "
3991       " IX 2VFV #VBV FS 7c4V#i HV \"W 3V !T         7V 0f @e >o Co 0\\ Dq <j Ly Fj ?h          (i  "
3992       "\\ ?Z @r :o\"s Hx Mt <q$X/X'X -X -X4Z$X -]0]0\\4X Im Np 9m Np ?q%~W$X/X-X(X,W5[6XAX1X+X.X%~S%"
3993       "a =V $a  ]   EV   6W    >W   Md   AW ,W ,W ,W ,W               HW             1b GV +b    "
3994       "     Fm   Dm      #`                \"j 4Z @x Mx Am 8X C~Q)X?X?X!m   9X   0V   0X   EX   'h"
3995       "  $W0W  \"h ?XGX 'g%g       0h%i :a   Cf :f *V   4m    %^ 0e   A^+V/^+V1f1V!X )Z /Z /Z /Z /"
3996       "Z 2` 1~V0o\"x Mx Mx Mx MX -X -X -X ,t J\\4X Im Bm Bm Bm Bm   FmHV-X/X'X/X'X/X'X/X-X.X\"X (l ;"
3997       "V $V 4UJU :ULXLU >XHX <UCU     =V $V 5VJV :XGX HV $V 4VKV :XGX +ZL\\ AULXLU ;V $V 3UJU :ULX"
3998       "LU >XHX     @V $V 2UJU 9XHX 3V =W &XHX              !` K~Z       >T   4S /a FaAa @T       "
3999       "  @w                Hl   KX 2VFV $WCV ES 8e5V$j HV \"V 1V \"T         7V 2j Eh ?q Dp 1\\ Dq >"
4000       "l Ly Hn Bj          +l %e E\\ At >s$v Kx Mt >u&X/X'X -X -X5Z#X -^2^0]5X Jo q ;o r Br%~W$X/X"
4001       "-X(X,X6[6XAY3Y+Y0Y%~S%W 3V  IW !_   FW   7W    >W   Md   AW ,W ,W ,W ,W               HW  "
4002       "           2[ ?V #[         Hn   En      #`                #l 6\\ Ax Mx Cp 9X C~Q)X?X?X\"o  "
4003       " ;Z   1V   1Z   FX  KS 0i  #W2W LV ,i ?XGX *l'h       3l'i ;c   Dg ;g ,W   6o    %^ 1g   B"
4004       "^,V.^,V0g3V X *\\ 1\\ 1\\ 1\\ 1\\ 2^ 0~V2s$x Mx Mx Mx MX -X -X -X ,v L]5X Jo Do Do Do Do   HpKW"
4005       "-X/X'X/X'X/X'X/X-Y0Y\"X )n <W &W 5VJV ;TI_ >XHX ;UEU     <W &W 5VIV ;XGX HW &W 5VIV ;XGX *g"
4006       " ?TI_ ;W &W 4VJV ;TI_ >XHX     @W &W 3VJV :XHX 4W =W &XHX     1\\ 1\\ 1\\ 1\\ 1\\ =XMV K~Y     "
4007       "  =S   4U 1c IdCc AU         Dz                In   LX 2VFV $VBV ES 9g7V$k HV #W 1W #T    "
4008       "     8W 3l Fh ?r Eq 3] Dq ?m Ly Ip Em          -n )k H\\ Au Av%x Mx Mt ?x(X/X'X -X -X6Z\"X -"
4009       "^2^0]5X Ls\"s ?s\"s Et%~W$X/X,X*X+X6[6X@Y5Y)Y2Y$~S%W 3W  JW \"a   FW   8W    >W   NZ   6W ,W "
4010       ",W ,W ,W               HW             2X <V  X         H[G[   Go       KZ                %"
4011       "[H[ 7\\ Ax Mx Ds ;X C~Q)X?X?X$s   >\\   2V   2\\   GX  KS 1j  #W2W LV -j ?XGX +ZEZ)VGY       "
4012       "5ZDZ)i <e   EUFY <UFX -W   7q    %VMU 2YIY   CVMU,V.VMU,V0UFX3V X *\\ 1\\ 1\\ 1\\ 1\\ 1\\ 0~W4v%"
4013       "x Mx Mx Mx MX -X -X -X ,x N]5X Ls Hs Hs Hs Hs   LsMW,X/X'X/X'X/X'X/X,Y2Y!X *\\G[ <W (W 4UHU"
4014       " <UH] =XHX ;VGV     ;W (W 5VHV ;XGX GW (W 4UGU ;XGX )c =UH] 9W (W 3UHU <UH] =XHX     ?W (W"
4015       " 2UHU :XHX 5W <W &XHX     5c 8c 8c 8c 8c @WKU J~X       >T   5V 2e KfEe CW         G|     "
4016       "           Jp   MX 2VFV $VBV ES 9XIX8V$l HV #V /V #T         8V 3n Gh ?s Fr 5^ Dq @n Lx Ir"
4017       " Go          .o -q L^ Bv Cx&z x Mt A{)X/X'X -X -X7Z!X -^2^0^6X Mu#t Au#t Gu%~W$X/X,X*X+X6["
4018       "6X?X5X'X2X#~S%W 2V  JW #c   FW   9W    >W   NX   4W ,W ,W ,W ,W               HW          "
4019       "   2W ;V  NW         IZCY   Hp       JY                &ZDZ 9^ Bx Mx Eu <X C~Q)X?X?X%u   @"
4020       "^   3V   3^   HX  KS 2k  \"W4W KV -ZGW ?XGX -X=X+R@W       8X<X  .XIX   FQ@W <Q@W /W   7dGU"
4021       "    %QHU 3XEX   DQHU-V-QHU-V/Q@W5V NX +^ 3^ 3^ 3^ 3^ 2\\ 0~W5x&x Mx Mx Mx MX -X -X -X ,z!^6"
4022       "X Mu Ju Ju Ju Ju   N}+X/X'X/X'X/X'X/X+X2X X +ZBY ;W *W 4UFU =TF\\ =XHX :VIV     9W *W 5VFV "
4023       "<XGX FW *W 4VGV <XGX (_ :TF\\ 8W *W 3UFU =TF\\ =XHX     >W *W 2UFU ;XHX 6W ;W &XHX     7h =h"
4024       " =h =h =h DWJV K~X       >T   5W 4g MgFg EY         J~                K]FZ   MX 2VFV $VBV "
4025       "ES :XGX9V%\\GX HV $W /W 3PATAP         GV 3[H[ Gh ?]F] GZE^ 6^ Dq A]FX Lx I\\F\\ G\\G[        "
4026       "  /[H] 0u N^ Bw E_D^&{!x Mt B`C_)X/X'X -X -X8Z X -_4_0_7X N^E^$u C^E^$u H^E\\%~W$X/X,Y,Y*W7"
4027       "]8X>Y7Y'Y4Y#~S%W 2V  JW $e   FV   9W    >W   NW   3W ,W ,W ,W ,W               HW         "
4028       "    2W ;V  NW         IY@X >X 4[AV       IX                &X@X 9^ Bx Mx F^E^ =X C~Q)X?X?X"
4029       "&^E^   B`   4V   4`   IX  KS 3\\GW  \"W4W KV .YBT ?XGX .V7V,P=W       :W8W  /VEV   3V +V /V "
4030       "  7eGU     KU 3WCW   ;U-V$U-V LV5V NX +^ 3^ 3^ 3^ 3^ 3^ 1~W6_D^&x Mx Mx Mx MX -X -X -X ,{\""
4031       "_7X N^E^ L^E^ L^E^ L^E^ L^E^  !^Ed*X/X'X/X'X/X'X/X+Y4Y X +Y?X ;V *V 4UDU >TEZ <XHX 9a     "
4032       "7V *V 4UDV =XGX EV *V 4VEV =XGX )] 7TEZ 6V *V 3UDU >TEZ <XHX     =V *V 2UDU <XHX 6V :W &XH"
4033       "X     9k @k @k @k @k EWJV K~W       >T   5Y 5g MhHi G[         M~Q                L\\AW   M"
4034       "X 2VFV $VCV DS :WEW:V%ZAU HV $V -V 3RCTCR         HW 4ZDZ H\\LX ?Y?[ HV>\\ 8_ DX )[?T -Y J[B"
4035       "[ I[CZ          0WAZ 2x ^ BX>^ G]=Z&X=b#X -X '];[)X/X'X -X -X:[ NX -_4_0_7X \\?\\%X@^ E\\?\\%X"
4036       "?] J[=X =X <X/X+X,X)X8]8X=Y9Y%Y6Y )Y$W 2W  KW %ZMZ   FV   :W    >W   X   3W     4W ,W     "
4037       "          HW             3X ;V  NX         KY?X Ca 9Y:R       HX                (X>X :VNV "
4038       "BZ /X '\\?\\ A^ FX0X)X?X?X'\\?\\   Db   5V   5b   JX  KS 3ZBT  !W6W JV .X?R   4V4U HV       ;V"
4039       "4V  1VCV   4V *U 0V   7fGU     KU 4WAW   <U.V#U.V JU6V MX +^ 3^ 3^ 3^ 3^ 3^ 2XIX F]=Z&X -X"
4040       " -X -X -X -X -X -X ,X=b$_7X \\?\\ N\\?\\ N\\?\\ N\\?\\ N\\?\\  #\\?`)X/X'X/X'X/X'X/X*Y6Y NX ,Y=W :V ,"
4041       "V 3UDU >TDX   ;a     6V ,V 4UBU   GV ,V 3UCU   0` 6TDX 4V ,V 2UDU >TDX       >V ,V 1UDU   "
4042       ":V 9W       (o Do Do Do Do GWIU J~V       >T   6Z 6i jIj I\\         N~R                M[="
4043       "U   MX 2VFV %VBV H] AWCW;V%Y=R HV %W -V 4UETEU         IV 4ZBZ IWGX ?V;[ IS9Z 9VNX DX *Z;R"
4044       " -X JZ>Y JZ?Y          1U>Z 5`C_#` CX;[ H[7W&X9_$X -X (\\6X)X/X'X -X -X;[ MX -_4_0`8X![;[&X"
4045       "=[ F[;[&X<[ LZ8U =X <X/X+X,X)X8]8X<X9X#X6X )Z$W 1V  KW &ZKZ   FV   ;W    >W   W   2W     4"
4046       "W ,W               HW             3W :V  MW         KX=W Cc ;X7P       HX                ("
4047       "W<W ;WNW BY /X ([;[ Gg JX0X)X?X?X([;[   Fd   6V   6d   KX  KS 4Y>R  !X8X JV /X<P   6V1U IV"
4048       "       <U0U  2UAU   3U *U 1V   6fGU     KU 4V?V   <U/V\"U/V IU7V LX ,` 5` 5` 5` 5` 5` 3XIX "
4049       "G[7W&X -X -X -X -X -X -X -X ,X9_%`8X![;[![;[![;[![;[![;[  %[;](X/X'X/X'X/X'X/X)X6X MX ,X;W"
4050       " :V .V 3UBU ?TBT   7]     3V .V 4VAU   GV .V 3UAU   4d 7TBT 1V .V 2UBU ?TBT       ;V .V 1U"
4051       "BU   <V 8W       )r Gr Gr Gr Gr IVHR GX+W       =S   5[ 7i!kJk I]        !^               "
4052       " )Y:T   MX 2VFV %VBV Le EVAV<V$X:P HV %W -W 6WFTFV         IV 4X?Y IRBX ?T7Y IP5Z :VNX DX "
4053       "+Z8P .Y JY<Y KY=X          1S;Y 6];\\$WNW CX9Z J[4U&X6]%X -X )[2V)X/X'X -X -X<[ LX -XNV6VNX"
4054       "0`8X\"Z7Z'X;Z HZ7Z'X;Z LY4R =X <X/X*X.X(X8]8X<Y;Y#Y8Y *Z#W 1V  KW 'ZIZ   FV   <W    >W   W "
4055       "  2W     4W ,W               HW             3W :V  MW         KW<X Dd <W       -W         "
4056       "       )W;X <WNW AY 0X )Z7Z Jl MX0X)X?X?X)Z7Z   Hf   7V   7f   LX  KS 4X;P   W8W IV /W   \""
4057       "V.U JV       >U.U  4VAV &V 5U *U 2V   6gGU     KU 5W?W   =U/V\"U/V IU7V LX ,WNW 5WNW 5WNW 5"
4058       "WNW 5WNW 5WNW 4XHX H[4U&X -X -X -X -X -X -X -X ,X6]&`8X\"Z7Z#Z7Z#Z7Z#Z7Z#Z7Z  'Z8['X/X'X/X'"
4059       "X/X'X/X)Y8Y MX ,W:W 9V 0V 3U@U     ?[     1V 0V 3U@V   GV 0V 3U?U   8h   1V 0V 2U@U       "
4060       "  CV 0V 1U@U   >V 7W       *`L` I`L` I`L` I`L` I`L` JV =X,X       >T   6] 9k\"lKl K_       "
4061       " #\\                'Y8S   MX 2VFV %VBV Nk IVAV=V$X 1V %V +V 6YHTHY -V       EW 5Y>Y :X ?R5"
4062       "Z .Y ;VMX DX +Y  DX IY<Y LY;X          2Q8Y 8[5[&WNW CX8Y KZ1T&X4\\&X -X *Z.T)X/X'X -X -X=["
4063       " KX -XNV6VNX0a9X#Z5Z(X:Y IZ5Z(X:Z NY1P =X <X/X*X.X'W9WNV:X:Y=Y!Y:Y *Z\"W 1W  LW (ZGZ      -"
4064       "W    >W   W   2W     4W ,W               HW             3W :V  MW         KW;W De =W      "
4065       " -X                *W:W <VLV @Y 1X *Z5Z Mp X0X)X?X?X*Z5Z   Jh   8V   8h   MX  KS 5Y   :X:X"
4066       " IV /W   #U+T JV       ?U+T  5U?U &V 5U +V     AgGU     KU 5V=V   =U0V!U0V IV8V KX ,WNW 5W"
4067       "NW 5WNW 5WNW 5WNW 5WNW 4XHX IZ1T&X -X -X -X -X -X -X -X ,X4\\'a9X#Z5Z%Z5Z%Z5Z%Z5Z%Z5Z  )Z5Z"
4068       "(X/X'X/X'X/X'X/X(Y:Y LX -X:W          !W                    2\\LZ                          "
4069       "EW       +[@[ K[@[ K[@[ K[@[ K[@[ KV <X-X     /P 0T   7^ 9k\"lLm La        %Z              "
4070       "  %Z6Q   MX 2VFV %VCV n KWAW>V$X 1V &W +W 5XITIX +V       EV 4X<X :X ?P2Y -X <WMX DX ,Y  C"
4071       "X JY:Y MX9W          2P7Y :Z0Z(WLW DX7X KY.R&X2Z&X -X *Y+R)X/X'X -X -X>[ JX -XNW8WNX0a9X#Y"
4072       "3Y(X9Y JY3Y(X9Y NX  LX <X/X*X.X'X:VMV:X9X=X NX:X *Z!W 0V  LW )ZEZ      .W    >W   W   2W  "
4073       "   4W ,W               HW             3W :V  MW         LX;W Df >W       ,W               "
4074       " +W8W >WLW @Y 2X +Z3Z!t\"X0X)X?X?X*Y3Y   Kj   9V   9j     AS 5X   8W:W HV /W   #T)T KV     "
4075       "  @T(T  6U?U &V 5T +V     AhGU     KU 5V=V   =U0V!U0V JV7V   WLW 7WLW 7WLW 7WLW 7WLW 7XNX "
4076       "6XGX IY.R&X -X -X -X -X -X -X -X ,X2Z'a9X#Y3Y%Y3Y%Y3Y%Y3Y%Y3Y  )Y3Z)X/X'X/X'X/X'X/X'X:X Ki"
4077       " >W8V                               *XHZ                          FW       ,Z<Z MZ<Z MZ<Z "
4078       "MZ<Z MZ<Z LV <X.X     .R 2S   7` :k#nMm Mb        &Z                $Y4P   MX 2VFV &VBV!o "
4079       "KV?V?V#W 0V &V )V 3XKTKX )V       EV 5X:X ;X  X -Y =VLX DX -Y  CY JY:Y NY9X           HX ;"
4080       "Z-Y)WLW DX7Y MY,Q&X1Z'X -X +Y)Q)X/X'X -X -X?[ IX -XMV8VMX0XNX:X$Y1Y)X9Y KY1Y)X8X NX  LX <X"
4081       "/X)X0X&X:VMV:X9Y?Y NY<Y *Y W 0V  LW *ZCZ      /W    >W   W   2W     4W ,W               HW"
4082       "             3W :V  MW         LW:W Dg ?W       ,X                ,W8W >WLW ?Y 3X +Y1Y\"v#X"
4083       "0X)X?X?X+Y1Y   MYNVNY   :V   :YNVNY     BS 5X   8X<X HV /W   $T?ZBT*c       AT&T  7U?U &V "
4084       "6U -W     @hGU     KU 6V;V   >U1V U1V KW7V   NWLW 7WLW 7WLW 7WLW 7WLW 7WLW 6XGX JY,Q&X -X "
4085       "-X -X -X -X -X -X ,X1Z(XNX:X$Y1Y'Y1Y'Y1Y'Y1Y'Y1Y P)P$Y3[)X/X'X/X'X/X'X/X'Y<Y Km BW8W      "
4086       "                         +UDZ               7P          1W       -Y8Y Y8Y Y8Y Y8Y Y8Y MV ;"
4087       "W.X     /T 4T   7a ;k#nMn Nc 6P :W4W ?Z ?X6X KY                #Y   0X 2VFV &VBV\"p KV?V?V#"
4088       "W 0V 'W )W 2XMTMX 'V       FW 5X:X ;X  Y -X >VKX DX -X  BX IX8X NX7W      KP  1P  =X <Y)X+"
4089       "XLX EX6X NY*P&X0Z(X -X ,Y'P)X/X'X -X -X@Z GX -XMV8VMX0XNX:X%Y/Y*X8X LY/Y*X8Y!X  KX <X/X)X0"
4090       "X&X:VMV:X8YAY LY>Y *Z W 0W  MW +ZAZ      0W    >W   W   2W     4W ,W               HW     "
4091       "        3W :V  MW         LW:W DSF[ @X       -X                -X8W ?WJW ?Y 4X ,Y/Y%z%X0X)"
4092       "X?X?X,Y/Y   YMVMY   ;V   ;YMVMY     CS 5X 5P*Q JW<W GV /W   %TBbET/g       BTGb?T  8U?U &V"
4093       " 7U 5_     ?hGU     KU 6V;V   >U2V NU2V$_7V   NXLX 9XLX 9XLX 9XLX 9XLX 8WLW 6XGX KY*P&X -X"
4094       " -X -X -X -X -X -X ,X0Z)XNX:X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y\"R+R&Y3]*X/X'X/X'X/X'X/X&Y>Y Jp EW:Y     "
4095       "                          +R@Y               7Q          2W       .XEVFY\"X5Y\"X5Y\"X5Y\"X5Y N"
4096       "V ;X/X     0V 5T   8c <k#nNo e >^ AW4W ?Z >W6W KY                \"Y   0X 2VFV &VCW#[LSKZ K"
4097       "V?V@V\"W 0V 'W )W 1XNTNX &V       FW 6Y:Y <X  NX -X ?WKX DX .Y  CY IX8X NX7W      NS  1S  @"
4098       "X =X&X,WJW EX6X NY /X/Y(X -X ,Y /X/X'X -X -XAZ FX -XMW:WMX0XMX;X%Y/Y*X8Y MY/Y*X8Y!X  KX <X"
4099       "/X)Y1X%W;WMW;W6XAX JX>X *Z NW 0W  MW ,Z?Z      1W    >W   W   2W     4W ,W               H"
4100       "W             3W :V  MW         LW:W DPAY ?Y       .W                -W6W @WJW >Y 5X ,X-X&"
4101       "_MXM_&X0X)X?X?X,Y/Y  !YLVLY   <V   <YLVLY     DS 6Y 6R,R JX>W FV /X   'TCfFT2i       CUGfB"
4102       "T  9U?U &V 7U 5]     >iGU     KU 6V;V   >U2V NU2V$]5V   NWJW 9WJW 9WJW 9WJW 9WJW 9WJW 8XFX"
4103       " KY /X -X -X -X -X -X -X -X ,X/Y)XMX;X%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y#T-T'Y3]*X/X'X/X'X/X'X/X%X>X Ir "
4104       "GW=\\                                GY               9S          3W       /XDVDX$X2X$X2X$X"
4105       "2X$X2X V ;X0X     0X 7T   8d <k#~`!g Bd DW4W ?[ ?X7W LY                !X   /X 2VFV &VCV#Z"
4106       "JSGV KV?VAV!W 0V 'V 'V /d $V       FV 5X8X <X  NX -X ?VJX DX .X  BX HX8X Y7X     #V  1V  C"
4107       "X >X$X-WJW EX6X Y .X.Y)X -X -Y .X/X'X -X -XBZ EX -XLV:VLX0XMX;X&Y-Y+X7X NY-Y+X7X!X  KX <X/"
4108       "X(X2X$X<VKV<X6YCY JY@Y +Z MW /V  MW -Y;Y    \"Z ;WDX 0Z 2XDW >Z <W !X :WDY     IW ,W  HX8X "
4109       "MY 3Z *X 3X &X 7] <W             3W :V  MW       ;X :W:W 4Y @[ )\\ (Y   6X     8QEV     :[ "
4110       "    JW6W @VIW =Y 6X -Y-Y(]JXJ]'X0X)X?X?X-Y-Y  #YKVKY   =V   =YKVKY     IZ 9X 6T.T JW>W FV "
4111       ".X   (TDgFT3j       CTFhDT  9U?U &V 8U 4\\     =iGU     KU 6V;V   >U3V MU3V#\\5V   MWJW 9WJW"
4112       " 9WJW 9WJW 9WJW 9WJW 8XFX LY .X -X -X -X -X -X -X -X ,X.Y*XMX;X&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y%V/V)Y3"
4113       "_+X/X'X/X'X/X'X/X%Y@Y Is HW?^ ?Z /Z /Z /Z /Z /Z /Z6Y NZ 0Z /Z /Z /Z         8Y 1Y 3Z /Z /Z"
4114       " /Z /Z   3ZCV          5WDX       DXCVCW%X0W%X0W%X0W%X0W V :X1X     0X 7T   9f =k#~`\"h Cf "
4115       "EW4W @\\ ?X8X LX                !Y   /X 2VFV 'VBV#XHSET KV?VAV!W 0V (W 'W .` \"V       GW 5X"
4116       "8X <X  NX -X @VIX DX .X  BX HX8X X5W     &Y  1Y  FX >W\"W.XJX FX6X X -X.Y)X -X -X -X/X'X -X"
4117       " -XCZ DX -XLV:VLX0XLX<X&X+X+X7X NX+X+X7X!X  KX <X/X(X2X$X<VKV<X5YEY HYBY +Z LW /W  NW .Y9Y"
4118       "    'b ?WG^ 7b 9^GW A` Gl 2_GW MWG_ DW ,W ,W8Y MW ,WG^>^4WG_ 9` @WG^ 9^GW MWG\\ ;f Gm <W6W#"
4119       "X2X#W;X;W5Y7Y#W1X\"u 6W :V  MW       >^BV\"W:W 3X ?^ 0e AWG_ KV.X ?X <W6W   HTG[ K}!WCWCW Ca"
4120       " 7p&{ NW6W AWHW >Z 7X -X+X)\\HXH\\(X0X)X?X?X-X+X  $YJVJY   >V   >YJVJY     Ma =X 7V0V JW@W E"
4121       "V .Y   *TEiET5k       DTEiDT  :VAV &V 9U 3_   ;W6W NiGU     KU 6V;V   >U3V MU3V#_8V   NXJX"
4122       " ;XJX ;XJX ;XJX ;XJX ;XJX :XEX LX -X -X -X -X -X -X -X -X ,X.Y*XLX<X&X+X+X+X+X+X+X+X+X+X&X"
4123       "1X*X3`+X/X'X/X'X/X'X/X$YBY Ht IW@_ Cb 7b 7b 7b 7b 7b 7b>a'b 7` 5` 5` 5` AW ,W ,W ,W  DY EW"
4124       "G_ 9` 5` 5` 5` 5` (Z <`GV W6W MW6W MW6W MW6W#W1X NWG^ HW1X     NWBVBW&W.W&WJP:PJW&W4PJW&W."
4125       "W!V :X2X     0X 6S   8g >k#~`#j Fj GW4W @\\ >W8W LX                 X   .X 2VFV 'VBV$XGSCR "
4126       "KV?VBV X 1V (W 'W ,\\  V       GW 5X8X <X  NX -X AWIX DX /X  BY HX8X X5W     ([  1[  HX ?W "
4127       "W/WHW FX6X!Y -X-Y*X -X .Y -X/X'X -X -XDZ CX -XLW<WLX0XKW<X'Y+X+X7X Y+X+X7X!X  KX <X/X'X4X#"
4128       "X<VKV<X4XFY FXBX *Y KW /W  NW /Y7Y    +g AWIb ;f =bIW De Il 3bIW MWIc FW ,W ,W9Y LW ,WIbBb"
4129       "6WIc >f CWIb =bIW MWI^ =j Im <W6W\"W2W\"W<Z<W4X7X!W2W!u 6W :V  MW       @bEW\"W:W 2X @c 8j CW"
4130       "Ic MX0W =W <W6W IW/W\"VI^ L}!WCWCW Ee =t&{ W4W BWHW =Y 7X .X*Y*ZFXFZ(X0X)X?X?X.Y+X  #WIVIW "
4131       "  =V   =WIVIW     f ?X 8X2X KW@W EV .Z   +SE[GVDS6ZDV       DSDVDXDS  9UAU %V :U 2`   <W6W"
4132       " NiGU     KU 6V;V   >U4V LU4V\"`:V GX /WHW ;WHW ;WHW ;WHW ;WHW ;WHW :XEX MY -X -X -X -X -X "
4133       "-X -X -X ,X-Y+XKW<X'Y+X,Y+X,Y+X,Y+X,Y+X'Z3Z,Y4WNY,X/X'X/X'X/X'X/X#XBX Gu JWB\\ Ag <g <g <g "
4134       "<g <g <gBe+f <e :e :e :e CW ,W ,W ,W  Mc FWIc >f ;f ;f ;f ;f +Z >eJU NW6W MW6W MW6W MW6W\"W"
4135       "2W MWIb IW2W     NWAVAW(W,W(WJR<RJW(W4RJW(W,W\"V 9W2X     1X 6T   9i ?k#~`#k Hl HW4W @] ?X9"
4136       "W LW                 NX   .X 2VFV 'VCW$WFSAP KV?VBV NW 1V (V &W *X  MV       GV 5X6X =X  N"
4137       "X -X AVHX DX /X  BX GX8X X5X     ,^  1^  LX ?W MW0WHW FX6X!X ,X-Y*X -X .X ,X/X'X -X -XEZ B"
4138       "X -XKV<VKX0XKX=X'Y+Y,X7X Y+Y,X7X!X  KX <X/X'X4X\"W=WKV<W3YGY FYDY +Z KW .V  NW 0Y5Y    /l C"
4139       "WJe ?j AeJW Eh Kl 5eJW MWJe GW ,W ,W:Y KW ,WJdDd7WJe @h DWJe AeJW MWJ_ ?l Im <W6W\"W2W!W=Z="
4140       "W2X9X W2W!u 6W :V  MW       BeFV!W;X 1W ?f =k CWJe NY2X =X =W6W JW-W$WI` N}!WCWCW Gi Av&{ "
4141       "W4W BVGW <Y 8X .X)X+ZEXEZ)X0X)X?X?X.Y+Y  #UHVHU   <V   <UHVHU    !j AX 9Z4Z KWBW DV -Z   -"
4142       "TFY@RDT8XAV       ETDVBWET  :VCV %V ;V )X   =W6W NiGU     KU 6V;V   >U5V KU5V GX<V FX /WHW"
4143       " ;WHW ;WHW ;WHW ;WHW ;WHW :WDX MX ,X -X -X -X -X -X -X -X ,X-Y+XKX=X'Y+Y-Y+Y-Y+Y-Y+Y-Y+Y'Z"
4144       "5Z+Y5WMY,X/X'X/X'X/X'X/X#YDY GX@^ KWCZ Al Al Al Al Al Al AlFh.j ?h =h =h =h EW ,W ,W ,W !g"
4145       " GWJe @h =h =h =h =h ,Z @hLV NW6W MW6W MW6W MW6W\"W2W MWJe KW2W     W@VAW)W+W)WJT>TKW)W4TKW"
4146       ")W+W\"V 9X3X     2X 5T   :k ?i\"~`$m Jn IW4W A^ ?X:X MW                 NY   .X 2VFV 7~X2XFS"
4147       " <V?VCV MX 2V )W %W +X  MV       GV 5X6X =X  NX -X BVGX DX /X  BX GX8X X5X LX -X  7a  1a  "
4148       "X @W KW2XHX GX6X!X ,X,X*X -X .X ,X/X'X -X -XFZ AX -XKV<VKX0XJW=X'X)X,X7X X)X,X7X!X  KX <X/"
4149       "X'X4X\"X>VIV>X2YIY DYFY +Z JW .V  NW 1Y3Y    1n DWLh Bm ChLW Gk Ll 6hLW MWKg HW ,W ,W;Y JW "
4150       ",WKfGg8WKg Cl FWLh ChLW MWK` @m Im <W6W\"X4X!W=Z=W1X;X NW3X!u 6W :V  MW       CgGV!W;W 0X ?"
4151       "g Am CWKg [4X >Y =W6W JW-W&YJb }!WCWCW Hk Dx&{ W4W CWFW <Y 9X /Y)X,ZDXDZ*X0X)X?X?X.X)X P #"
4152       "SGVGS %P 7V 9P0P CSGVGS    !l BX 8ZGWFZ JWCX DV ,Z   .SEW<PCS8V?V .P>P     JSCVAVDS  :WEV "
4153       "$V <V &W   >W6W NiGU     KU 6V;V BP>P /U5V KU5V EW=V FX 0XHX =XHX =XHX =XHX =XHX =XHX <XDX"
4154       " MX ,X -X -X -X -X -X -X -X ,X,X+XJW=X'X)X-X)X-X)X-X)X-X)X&Z7Z*X5WKX,X/X'X/X'X/X'X/X\"YFY F"
4155       "X=[ KWDY @n Cn Cn Cn Cn Cn CnHj1m Bk @k @k @k FW ,W ,W ,W $j GWKg Cl Al Al Al Al .Z Bs MW6"
4156       "W MW6W MW6W MW6W\"W3X MWLh LW3X     V?V@W*V)W*VJV@VKW*V4VKW*V)W#V 9X4X     2X 4S   :l ?i\"~`"
4157       "%o Lp JW4W A^ >W:X MW                 NX   -X 2VFV 7~X2WES <V?VDV LX 2V )W %W -\\  V       "
4158       "HW 5X6X =X  NX .X BWGX DX 0X  BY FX:X NX5X LX -X  :d  1d $Y @V IV2WFW GX6X\"Y ,X,Y+X -X /Y "
4159       ",X/X'X -X -XH[ @X -XKW>WKX0XJX>X(Y)X,X7X!Y)X,X7X!Y  LX <X/X&X6X!X>VIV>X1YKY BXFX +Z IW .W "
4160       " W 2Y1Y    2o EWMj Dn DjMW Hn Nl 7jMW MWLi IW ,W ,W<Y IW ,WLhIi9WLi En GWMj EjMW MWLa An I"
4161       "m <W6W!W4W W=Z=W1Y=Y MW4W u 6W :V  MW       DiIV W;W /W =g Cm CWLi![4W =Y =W6W KW+W(ZKd!}!"
4162       "WCWCW Im Fy&{ W4W CWFW ;Y :X /X'X-YCXCY*X0X)X?X?X/Y)X!R #QFVFQ $R 9V :R1R DQFVFQ    \"n BX "
4163       "7ZJ\\JZ HWDW CV +[   1TFW.T:W?V /Q?Q     KTCVAWET  :XIX $V =V #U   >W6W NiGU     KU 6V;V BQ"
4164       "?Q 0U6V JU6V BU>V EX 0WFW =WFW =WFW =WFW =WFW =WFW <XDX NY ,X -X -X -X -X -X -X -X ,X,Y,XJ"
4165       "X>X(Y)X.Y)X.Y)X.Y)X.Y)X%Z9Z*Y6WJX,X/X'X/X'X/X'X/X!XFX EX;Z LWDX ?o Do Do Do Do Do DoKn4n C"
4166       "n Cn Cn Cn HW ,W ,W ,W %l HWLi En Cn Cn Cn Cn /Z Cs LW6W MW6W MW6W MW6W!W4W LWMj LW4W     "
4167       "W?V?V+W(V+WKXBXKV+W5XKV+W(V$W 8W4X     2X 5T   ;n ?g!~_%p LZDZ JW4W A^ >W:W MW            "
4168       "     MX   -X 2VFV 7~X2WES <WAWDV KX 3V )W %W /` \"V       HV 4X6X =X  Y .X BVFX DX 0X  BX E"
4169       "X:X NX5X LX -X  <e  /e 'Y @V GV4XFX HX7X!X +X+X+X -X /X +X/X'X -X -XI[ ?X -XJV>VJX0XIW>X(X"
4170       "'X-X7X!X'X-X7X!Y  LX <X/X&X6X!X>VIV>X1YKY AXHX +Z HW -V  W 3Y/Y    3p FWMk Fo EkMW Io Nl 8"
4171       "kMW MWMk JW ,W ,W=Y HW ,WMjJj:WMk Gp HWMk GkMW MWMb Bo Im <W6W!W4W W>\\>W0X=X LW5X u 6W :V "
4172       " MW       EkJV W<X /W >j Fn CWMk\"\\6X =Z >W6W KW+W)[Ke\"}!WCWCW Jo Hz&{ W4W DWDW ;Y ;X /X'X."
4173       "YBXBY+X0X)X?X?X/X'X#T  HV  IT :V ;T3T :V   CV +o BX 6ZM`MZ GXFX CV *\\   3SFW,S:V>V 0R@R   "
4174       "  KSBV@VDS  9e #V ?W \"V   ?W6W NiGU     KU 6V;V BR@R 1U6V JU6V BV?V EX 1XFX ?XFX ?XFX ?XFX"
4175       " ?XFX ?XFW =XCX NX +X -X -X -X -X -X -X -X ,X+X,XIW>X(X'X/X'X/X'X/X'X/X'X%Z;Z)X5VHX-X/X'X/"
4176       "X'X/X'X/X XHX DX:Y LWEX >p Ep Ep Ep Ep Ep EpMp6o Do Do Do Do HW ,W ,W ,W 'o IWMk Gp Ep Ep "
4177       "Ep Ep 0Z Ds KW6W MW6W MW6W MW6W!W5X LWMk MW5X     V>V?W,V'W,VKZDYKW,V5YKW,V'W%W 8X5W     2"
4178       "X 4T   ;o @g ~^%q NY@Y KW4W B` ?X<X MV                 LX   -X 2VFV 7~X2WES ;VAVDV JY 4V )"
4179       "V $W 1d $V       HV 4X6X =X  X .Y CWFX DXLY =XEX 'Y EY<X MX5X LX -X  ?e  )e +Y ?V:X6V4WDW "
4180       "HX7X!X +X+X+X -X /X +X/X'X -X -XJ[ >X -XJW@WJX0XIX?X(X'X-X7X!X'X-X8Y Y  MX <X/X%W6X W?WIV>"
4181       "W/YMY @YJY +Y GW -V  W 4X+X    4YE\\ FWNXG\\ H]EX F\\GXNW J\\F[ GW ,\\GXNW MWNXG[ JW ,W ,W?Z GW"
4182       " ,WNXH[KXH[:WNXG[ H]H] IWNXG\\ I\\GXNW MWNXFQ C\\CW CW ,W6W!X6X NW?\\?W.X?X JW6W 1X 6W :V  MW "
4183       "    9X=X\"[IZKW W=Y /W @m H]DV CWNXG[\"\\6W =[ >W6W LW)W*ZJWKY\"}!WCWCW K\\H] J{&{ V3W DWDW :Y "
4184       "<X /X'X.XAXAX+X0X)X?X?X/X'X$V  IV  JV ;V <V5V ;V   CV ,^MSKW BX 5x EWFW BV ,_   5TFW,S:V?W"
4185       " 1SAS     LTBV@VDS  9d \"V @W  U   ?W6W NiGU     KU 5V=V ASAS 2U7V IU7V @U@V DX 1WDW ?WDW ?"
4186       "WDW ?WDW ?WDW ?XFX >XCX NX +X -X -X -X -X -X -X -X ,X+X,XIX?X(X'X/X'X/X'X/X'X/X'X$Z=Z(X6WH"
4187       "X-X/X'X/X'X/X'X/X YJY DX9Y MWEW =YE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE\\ EYE]N\\G[7]EX E\\F[ F\\F[ F\\F[ "
4188       "F\\F[ IW ,W ,W ,W (p IWNXG[ H]H] G]H] G]H] G]H] G]H] 1Z E]H^ JW6W MW6W MW6W MW6W W6W KWNXG\\"
4189       " MW6W     NV>V>V,V&V,VJZFYIV,V6YIV,V&V%W 7W6X     3X LR:T   ;q @e N~^&s!Y>Y LW4W B` >W<X N"
4190       "W                $x   FX 2VFV 7~X2WES ;VAVEW IY 5V *W #W 4XNTNX &V       IW 5X5X =X  X .X "
4191       "CWEX Di AXH_ +X CX<X MX5X LX -X  Be  #e /Z @V<^IUDV5WDW HX8Y!X +X+X+X -X /X +X/X'X -X -XK["
4192       " =X -XIV@VIX0XHW?X(X'X-X7X!X'X-X8X NZ  NX <X/X%X8X NX@VGV@X.c >XJX +Z GW -W !W 5X)X    5U>"
4193       "Z G_CZ I[>T FZC_ KZAZ HW -ZB_ M^BZ KW ,W ,W@Z FW ,^CZMVCZ;^BZ IZBZ I_CZ IZC_ M^ 5Y<S CW ,W"
4194       "6W W6W MW?\\?W.YAY JW6W 2Y 6W :V  MW     ;\\A\\%YDYLV NW>Y .W AXJa IZ<Q C^BZ MX8X =\\ ?W6W LW)"
4195       "W+YIXJY LW=W JWCWCW LZBZ K]F] ;W >W2W EWDW 9Y =X /X'X/YAXAY,X0X)X?X?X/X'X%X  JV  KX <V =X7"
4196       "X <V   CV -\\JSHT BX 4v DXHX BV -b   7SEV*S;V?W 2TBT     LSAV@VCS  9b !V AV  MU   ?W6W MhGU"
4197       "     KU 5V=V ATBT 3U8V HU8V ?UAV CX 1WDW ?WDW ?WDW ?WDW ?WDW ?WDW ?XBX NX +X -X -X -X -X -"
4198       "X -X -X ,X+X,XHW?X(X'X/X'X/X'X/X'X/X'X#Z?Z'X7WGX-X/X'X/X'X/X'X/X NXJX CX9Y MWFW <U>Z FU>Z "
4199       "FU>Z FU>Z FU>Z FU>Z FU>eBZ9[>T FZAZ HZAZ HZAZ HZAZ JW ,W ,W ,W )r J^BZ IZBZ GZBZ GZBZ GZBZ"
4200       " GZBZ 1Z EZB[ JW6W MW6W MW6W MW6W W6W K_CZ MW6W     V=V>V-V%V-VHZHYHV-V6YHV-V%V%W 7X7X    "
4201       " 4X NU:T   <s Ae N~^'u\"X<X LW4W BWNW >W<W MW                $w   EX   2~X2WES ;WCWEV GY   "
4202       "9W #W 5XMTMX 'V       IV 4X4X >X !Y 0Y BVDX Dk CXJc -X BX>X LX5Y MX -X  Ee   Le 3Z ?U=bKUC"
4203       "U6XDX IX9Y X +X+X+X -X /X +X/X'X -X -XL[ <X -XIV@VIX0XHX@X(X'X-X8Y!X'X-X8X N[  X <X/X%X8X "
4204       "NX@VGV@X.c =XLX +Z FW ,V !W       AR9Y H]?Y KZ:R GY?] LY=Y IW -Y?] M]@Y KW ,W ,WAY DW ,]@X"
4205       "NV@X;]@Y JY>Y J]?Y KY?] M] 4X8P CW ,W6W X8X MW?\\?W-XAX IW7X 3Y 5W :V  MW     =_C_(YBXLV NW"
4206       "?Z -W CXC\\ KY ,]@Y LW8X >] ?W6W LW)W,YHWHY MW=W JWCWCW MY>Y L[B[ ;W >W2W FWBW 9Y >X 0X%X0X"
4207       "@X@X,X0X)X?X?X/X'X&Y  JV  KY =V >Y7Y =V   CV .[HSFR BX 3t BWHW AV .WN\\   9SFV)S;V?W 3UCU  "
4208       "   LSAV@VCS  7_  V BV  LU   ?W6W MhGU     KU 5W?W AUCU 4U8V HU8V ?UAV CX 2XDX AXDX AXDX AX"
4209       "DX AXDX AXDX @XBX NX +X -X -X -X -X -X -X -X ,X+X,XHX@X(X'X/X'X/X'X/X'X/X'X\"ZAZ&X8WFX-X/X'"
4210       "X/X'X/X'X/X MXLX BX8X MWFW <R9Y GR9Y GR9Y GR9Y GR9Y GR9Y GR9a>Y;Z:R GY=Y JY=Y JY=Y JY=Y KW"
4211       " ,W ,W ,W *]E[ J]@Y JY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y JW6W MW6W MW6W MW6W W7X K]?Y NW7X    "
4212       " V=V=U-V$U-VGZJYFU-V7YFU-V$U%W 7X8X    &~X/X:T   =t @c L~\\'v\"W:W LW4W CXNX ?X>X MV        "
4213       "        $x   EX   2~X2WES :VDWEV FZ   :W #W 7XKTKX )V       IV 4X4X >X !X 0Y BWDX Dm FXKf "
4214       "/Y AYBY KX5Y MX -X  Gd ~X d 5Y ?V>dLUCU6WBW IX;Z Y +X+Y,X -X 0Y +X/X'X -X -XM[ ;X -XIWBWIX"
4215       "0XGW@X)Y'Y.X8X!Y'Y.X9Y M] #X <X/X$X:X MX@VGV@X-a <YNY ,Z EW ,V !W       AP6X H\\=Y LY7P HY="
4216       "\\ LX;X IW .Y=\\ M[=X KW ,W ,WBY CW ,[=]=W;[=X KY<Y K\\=Y MY=\\ M\\ 4X *W ,W6W NW8X MW@VNV@W,XC"
4217       "X GW8W 3Y 4W :V  MW     >aEa)X@XNW NWA[ ,W DW?[ LX +[=X KW:X =] ?W6W MW'W-XGWGX MW=W JWCWC"
4218       "W MX<Y NZ>Z <W >W2W FWBW 9Z ?X 0X%X0X@X@X,X0X(X@X@X/Y'Y(Y  IV  JY >V ?Y5Y >V   CV .YFSDP B"
4219       "X 2q @XJX AV /WK[   :SFV)S;V@X 4VDV     LSAV@VCS  6\\  MV CV  KU   ?W6W MhGU     KU 4V?V @V"
4220       "DV 5U9V GU9V >UBV BX 2WBW AWBW AWBW AWBW AWBW AXDX @XBX Y +X -X -X -X -X -X -X -X ,X+Y-XGW"
4221       "@X)Y'Y1Y'Y1Y'Y1Y'Y1Y'Y\"ZCZ&Y9WEY.X/X'X/X'X/X'X/X MYNY BX8Y NWFW <P6X GP6X GP6X GP6X GP6X G"
4222       "P6X GP6_<X;Y7P GX;X JX;X JX;X JX;X KW ,W ,W ,W *Z?Y K[=X KY<Y KY<Y KY<Y KY<Y KY<Y 3Z GY<Y "
4223       "KW6W MW6W MW6W MW6W NW8W J\\=Y NW8W     NV=V=V.V$V.VFZLYEV.V8YEV.V$V&W 6W8X    &~X2\\<T   =v"
4224       " Ab K~\\(x$W8W MW4W CXNX ?X>X NW                $w   DX   $VBV#XFS :WFXEV H]   ;W #W 9XITIX"
4225       " +V       JW 4X4X >X \"Y 3[ BWCX Dn GXLi 1X ?ZFZ JY7Z MX -X  Je M~X Me 9Y >U?gMUCV7WBW IX>\\"
4226       " NX *X*X,X -X 0X *X/X'X -X -XNZ 9X -XHVBVHX0XGXAX)X%X.X9Y!X%X.X:Y La 'X <X/X$X:X LWAWGV@W+"
4227       "_ :XNX ,Z DW ,W \"W       &W H[;X MY .X;[ MX9X JW .X;[ M[<X LW ,W ,WCY BW ,Z<\\<X<[<X LX:X K"
4228       "[;X MX;[ M[ 3W )W ,W6W NW8W KWAVNVAW*XEX FW9X 4Y 3W :V  MW     ?cGc+Y?WNV MWD] +W DV=Z LX "
4229       "+Z;X LW:X >_ @W6W MW'W.YGWFX NW=W JWCWCW NX:X NY<Y <W >W2W FWBW 8Z @X 0X%X0X@X@X,X0X(X@X@X"
4230       "/X%X)Y  HV  IY ?V @Y3Y ?V   CV /YES 6X 1\\H[ JcJc LV 0WI\\   =TFV)S;WAX 5WEW     MTAVAWCS  3"
4231       "W 4~W.W  KV   ?W6W LgGU     KU 4WAW @WEW 6U9V GU9V ?VBV BX 2WBW AWBW AWBW AWBW AWBW AWBW A"
4232       "XAX X *X -X -X -X -X -X -X -X ,X*X-XGXAX)X%X1X%X1X%X1X%X1X%X!ZEZ%X9WCX.X/X'X/X'X/X'X/X LXN"
4233       "X AX7X NWFW !W ,W ,W ,W ,W ,W ,]:X=Y .X9X LX9X LX9X LX9X LW ,W ,W ,W +Z=X K[<X LX:X KX:X K"
4234       "X:X KX:X KX:X 3Z GX<Z KW6W MW6W MW6W MW6W NW9X J[;X NW9X     NU<V=V.U#V.UDZNYDV.U8YDV.U#V&"
4235       "V 5X9W    %~X3]<T   >x A` J~\\(y%W8W MW4W CXMW >W>W MV                $x   DX   $VCV\"XFS 9X"
4236       "IXEV H_   <W #W ;YHTHY -V       JV 3X4X >X #Y ?g AVBX Do HXMk 3Y >l HX7Z MX -X  Me J~X Je "
4237       "=Y >V?hNUBU8XBX Ju MX *X*X,w Lq IX *~R'X -X -c 8X -XHVBVHX0XFWAX)X%X.X9Y!X%X.X;Z Ke ,X <X/"
4238       "X$X:X LXBVEVBX+_ 9` +Y CW +V \"W       %W IZ9X NX .X9Z MW7W JW /X9Z MZ;X LW ,W ,WDY AW ,Z;["
4239       ";W<Z;X MY:Y LZ9X X9Z MZ 2W )W ,W6W NX:X KWAVNVAW*YGY EW:W 4Z 3W :V  MW     ?XMYIe,X>WNV MW"
4240       "Ib +W EW;Y MW *Z;X KV:W =_ @W6W NW%W/XFWFX NW=W JWCWCW NW8X!Y:Y =W >| GW@W 8Y @X 0X%X1Y@X@"
4241       "Y-X0X(X@X@X/XImIX*Y  GV  HY @V AY1Y @V   CV /XDS 6X 0YDY JdLd LV 1WF[   >SFV'S<WBY 6XFX   "
4242       "  MS@VAVAS    @~W/W  JU   >W6W LgGU     KU 3WCW ?XFX 7U:V FU:V >UBV AX 3XBX CXBX CXBX CXBX"
4243       " CXBX CXBX BXAw?X *w Lw Lw Lw LX -X -X -X ,X*X-XFWAX)X%X1X%X1X%X1X%X1X%X ZGZ$X:WBX.X/X'X/X"
4244       "'X/X'X/X K` @X7X NWFW  W ,W ,W ,W ,W ,W ,[8W=X -W7W LW7W LW7W LW7W LW ,W ,W ,W ,Y:X LZ;X M"
4245       "Y:Y MY:Y MY:Y MY:Y MY:Y  \"Y=\\ LW6W MW6W MW6W MW6W MW:W IZ9X NW:W     NV<V=V/V#V/VCcCV/V9YC"
4246       "V/V=X>V&V 4W:X    %~X2TNV<S   =y KWM^LW$~Z({&W7V MW4W CWLX ?X?W MV                 KX   ,X"
4247       "   %VBV!XGS 9gFV Ha   >W \"W ;WFTFW -V       JV 3X4X >X #Y ?f AWBX Dp IXNm 4X <j GX7Z MX -X"
4248       " !e G~X Ge AY =U?ZH^BU8W@W Jt LX *X*X,w Lq IX *~R'X -X -b 7X -XHWDWHX0XFXBX)X%X.X:Y X%X.X<"
4249       "Z Ih 0X <X/X#X<X KXBVEVBX*] 8` ,Z CW +V \"W       %W IZ9X X -X9Z NX7X KW /X9Z MY9W LW ,W ,W"
4250       "EY @W ,Y:Z:W<Y9W MX8X LZ9X X9Z MY 1W )W ,W6W MW:W JWAVNVAW)XGX DW:W 4Y 3X :V  MW     @VHXK"
4251       "WGV,W<^ MWIa *W FW9Y NW *Y9W KW<X >` @W6W NW%W/WEWEW NW=W JWCWCW X8X!X8X =W >| GW@W 7Y AX "
4252       "0X%X1X?X?X-X0X(X@X@X/XImIX+Y  FV  GY AV BY/Y AV   DX 1XCS 6X 0W@X KdLd LV 1VCZ   ?SFV'S;WE"
4253       "[ 7XFX G~X  .S@VBWAS    @~W0W .P>W   >W6W KfGU     KU 3XEX >XFX 8U;V:W3U;VCZ9P>WCV:W/Y 3W@"
4254       "W CW@W CW@W CW@W CW@W CXBX CX@w?X *w Lw Lw Lw LX -X -X -X 5p9X-XFXBX)X%X1X%X1X%X1X%X1X%X N"
4255       "ZIZ#X:VAX.X/X'X/X'X/X'X/X K` @X7X NWFW  W ,W ,W ,W ,W ,W ,[8X?X -X7X NX7X NX7X NX7X MW ,W "
4256       ",W ,W ,X9X LY9W MX8X MX8X MX8X MX8X MX8X  \"X=] LW6W MW6W MW6W MW6W MW:W IZ9X NW:W     NVLu"
4257       "KU/VLuKU/VBaAU/V:YAU/V=X=U&V 4X;X    %~X2RLW>T   >{!z'~Z)}(W6W NW4W DXLX ?X@X MV          "
4258       "       KX   ,X   %VBV!YHS 8eEV Ic   ?W !W ;UETEU ,V       KW 3X4X >X $Y >c ?WAX DWD^ JbG] "
4259       "5X 9d DY9[ MX -X #d D~X Dd DY <U@YD\\BU9X@X Kq IX *X*X,w Lq IX *~R'X -X -a 6X -XGVDVGX0XEWB"
4260       "X)X%X.X;Z X%X.X?\\ Gk 4X <X/X#X<X KXBVEVBX)[ 6^ ,Z BW +W #W       %W IY7W X -W7Y NW5W KW 0X"
4261       "7Y MY9W LW ,W ,WFY ?W ,Y:Z:W<Y9W MW6W LY7W W7Y MY 1W )W ,W6W MW:W JWBVLVBW(XIX CW;X 5Y 2X "
4262       ":V  MX     BUDVKVDU.X<] LWI_ :WEW FV7X NW *Y9W JV<X >a AW6W NW%W0XEWEX W=W JWCWCW W6W!X8X "
4263       "=W >| HX@X 7Y BX 0X%X1X?X?X-X0X(X@X@X/XImIX,Y  EV  FY BV CY-Y BV   DX 1XCS 6X 1W>W KeNe LV"
4264       " 1VB[   ASFV'S;YI] 9YGY F~X  .S@VDX@S    @~W1V ,TEZ   >W6W JeGU IX   +U 2YIY <YGY :U;V:W3U"
4265       ";VGa<TEZCV:W/X 3X@X EX@X EX@X EX@X EX@X EX@X DX@w?X *w Lw Lw Lw LX -X -X -X 5p9X-XEWBX)X%X"
4266       "1X%X1X%X1X%X1X%X MZKZ\"X;WAX.X/X'X/X'X/X'X/X J^ ?X7X NWFX !W ,W ,W ,W ,W ,W ,Z6W?X -W5W NW5"
4267       "W NW5W NW5W MW ,W ,W ,W -X7W LY9W MW6W MW6W MW6W MW6W MW6W  \"W=^ LW6W MW6W MW6W MW6W MW;X "
4268       "IY7W NW;X     NVLuKU/VLuKU/VA_@U/V;Y@U/V=X=U&V 4X<X    $~X,W>T   ?|\"}(~X)~(W6W NW4W DXKW >"
4269       "W@X MV                 KX   ,X   %VBV!ZIS 7cEV IYNZ8W  0W !W :RCTCR +V       KW 3X4X >X %Y"
4270       " =b >V@X DS=\\ K`C[ 6Y 8b BX9[     Nd A~X Ad HY <VAX@ZBV:X?W Kq IX *X*X,w Lq IX *~R'X -X -a"
4271       " 6X -XGVDVGX0XEXCX)X%X.X=[ NX%X.u Fl 6X <X/X\"W<W IWCWEVBW([ 5\\ ,Z AW +W #W       $V IY7X\"X"
4272       " -X7Y NW5W KW 0X7Y MX8X MW ,W ,WHZ >W ,X8X8W=X8X X6X MY7X\"X7Y MX 0W )W ,W6W MX<X IWCVLVCW&"
4273       "XKX AW<W 5Y 1W 9V  LW  4P  /TBVMVBT.X;\\ LWI` =\\HW GW7X NW *X8X KV=X >XMW AW6W NW%W0XEWDW W"
4274       "=W JWCWCW!X6X#X6X >W >| HW>W 6Y CX 0X%X1X?X?X-X0X'XAXAX.XImIX-Y  DV  EY CV DY+Y CV   DX 2X"
4275       "BS 6X 1V<V KeNe LV 2V?Y   ASFV'S:dNV :XFY E~X  .S@i?S    @~W2i >h   =W6W JeGU IX   4g :g :"
4276       "YFX DgEV:X<gEVHe>hCV:X/X 3X?W EX?W EX?W EX?W EX?W EX@X EX?w?X *w Lw Lw Lw LX -X -X -X 5p9X"
4277       "-XEXCX)X%X1X%X1X%X1X%X1X%X LZMZ!X<W@X.X/X'X/X'X/X'X/X I\\ >X7X NWFY !V +V +V +V +V +V +Y6W@"
4278       "X ,W5W NW5W NW5W NW5W MW ,W ,W ,W -X7X MX8X X6X X6X X6X X6X X6X  $X=_ MW6W MW6W MW6W MW6W "
4279       "LW<W HY7X NW<W     MVLuKU/VLuKU/V@]?U/V<Y?U/V=X=U&V 3W<X    $~X+V>S   >}%~R)~V(~P)W6W NW4W"
4280       " DWJX ?XAW L~^               $X   ,X   %VCV N\\LS 6aDVAW0XLZ9W  0W !W :PATAP +V       KV 2X"
4281       "4X >X &Z =e BW@X DP8[ L^?Z 7X :h EY;\\    \"d >~X ?e LY ;U@W>YAU:W>W Ks KX *X*X,w Lq IX6f+~R"
4282       "'X -X -b 7X -XGWFWGX0XDWCX)X%X.X@^ NX%X.s Bl 8X <X/X\"X>X IXDVCVDX)[ 4\\ -Z @W *V #W       $"
4283       "W JX5W\"X -W5X W4W KW 0W5X MX7W MW ,W ,WIZ =W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LW<W HW"
4284       "CVLVCW&YMY AW=X 6Y 1X 9V  LX 1X.Q  /TA]AU/W:\\ LWIb A`JW GV5X NW +X7W KW>X >XMX BW6W W#W1WD"
4285       "WDW W=W JWCWCW!W4W#X6X >W >| HW>W 7Y BX 0X%X1X?X?X-X0X'XAXAX.XImIX.Y  CV  DY DV EY)Y DV   "
4286       "DX 2XBS 6X 2W<W =^ =V 2V>Y   BSFV'S9bMV ;XFY D~X  .S@h>S    @~W2i >g   <W6W HcGU IX   4g 9"
4287       "e 8YFX EgEV;Y<gEVHf?gBV;Y0Y 3W>W EW>W EW>W EW>W EW>W EW>W EX?w?X *w Lw Lw Lw LX -X -X -X 5"
4288       "p9X-XDWCX)X%X1X%X1X%X1X%X1X%X Ke X=W?X.X/X'X/X'X/X'X/X I\\ >X7X NWEY \"W ,W ,W ,W ,W ,W ,X5W"
4289       "@X -W4W W4W W4W W4W MW ,W ,W ,W -W6X MX7W W4W W4W W4W W4W W4W  $W=VMW MW6W MW6W MW6W MW6W "
4290       "LW=X HX5W NW=X     MVLuKU/VLuKU/V?[>U/V=Y>U/V=X=U&V 3X=W     7X FW@T   ?~&~T*~V)~R*W5V NW4"
4291       "W EXJX ?XBX L~^               $X   ,X   &VBV Mb 4]CVC]4XJZ:W  0W !W +T  KV       KV 2X4X >"
4292       "X 'Z <g EW?X +Z L]=Z 9Y <l GZ=]    %e    e!Y :UAW<XAU;X>X Lu MX *X*X,w Lq IX6f+~R'X -X -c "
4293       "8X -XFVFVFX0XDXDX)X%X.u MX%X.r ?l :X <X/X\"X>X IXDVCVDX)\\ 4Z ,Y ?W *V #W       $W JX5W\"W ,W"
4294       "5X W3W LW 0W5X MX7W MW ,W ,WJY ;W ,X8X8W=X7W W4W MX5W\"W5X MX 0X *W ,W6W LW<W HWCVKUCW%XMX "
4295       "?W>W 6Y 0X 9V  LX 5`3R  0T?[?T/W:[ KWId DbKW HW5X NW +X7W JV>W =WLX BW6W W#W1WDWDW W=W JWC"
4296       "WCW!W4W#W4W >W >| IX>X 9Y AX 0X%X1X?X?X-X0X'XAXAX.XImIX/Y  BV  CY EV FY'Y EV   DX 2WAS ?r "
4297       "CV:V =^ =V 2V=Y   CSFV'S8`LV <XFX B~X  .S@e;S    @~W2i >e   :W6W GbGU IX   4g 8c 5XFX FgFV"
4298       ":Y<gFVGg@eAV:Y1Y 3X>X GX>X GX>X GX>X GX>X GX>X FX?w?X *w Lw Lw Lw LX -X -X -X 5p9X-XDXDX)X"
4299       "%X1X%X1X%X1X%X1X%X Jc NX>W>X.X/X'X/X'X/X'X/X HZ =X7X NWEZ #W ,W ,W ,W ,W ,W ,X4WAW ,W3W!W3"
4300       "W!W3W!W3W NW ,W ,W ,W .X5W MX7W W4W W4W W4W W4W W4W  $W>VLW MW6W MW6W MW6W MW6W KW>W GX5W "
4301       "MW>W     LVLuKU/VLuKU/V>Z>U/V>Y=U/V=X=U&V 2W>X     8Y FW@T   ?~P(~V*~T(~Q)V4V NW4W EXJX >W"
4302       "BX L~^               $X   ,X   &VBV Ld 4WAVD`6XHZ;W  0W !W +T  KV       LW 2X4X >X 'Y ;i G"
4303       "V>X *Z M\\;Y 9X =p HZ?^    'd    Id$Y 9UAW<XAU;W<W Lw X *X*X,w Lq IX6f+~R'X -X -d 9X -XFVFV"
4304       "FX0XCWDX)X%X.t LX%X.p ;k ;X <X/X!X@X HXDVCVDX*^ 4X ,Z ?W *W $W       $W JX5W\"W ,W5X W3W LW"
4305       " 0W5X MW6W MW ,W ,WKY :W ,W7W7W=W6W W4W MX5W\"W5X MX /Y ,W ,W6W LX>X GWEVJVEW#a >W>W 7Y 1Y "
4306       "8V  KY 9e8T  0T?Z>T0X:[ KWIf GdLW HW4W MW ,W6W JV?X >XKW BW6W W#W2XDWDX!W=W JWCWCW!W4W#W4W"
4307       " >W >| IW<W :Y @X 0X%X1X?X?X-X0X&XBXBX-XImIX0Y  AV  BY FV GY%Y FV   DX 2WAS ?r DW:W =\\ <V "
4308       "2V;W   CSFV'S7]JV =XFX A~X  .S@d:S    (V Ii <a   8W6W FaGU IX   4g 6_ 2XFX GgGV:Z<gGVFUFY?"
4309       "a@V:Z2Y 2W<W GW<W GW<W GW<W GW<W GX>X GX>w?X *w Lw Lw Lw LX -X -X -X 5p9X-XCWDX)X%X1X%X1X%",
4310 
4311       // Second string:
4312       "X1X%X1X%X Ia MX?W=X.X/X'X/X'X/X'X/X GX <X7X NWDZ $W ,W ,W ,W ,W ,W ,X4WAW ,W3W!W3W!W3W!W3W"
4313       " NW ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W  $W?VKW MW6W MW6W MW6W MW6W KW>W GX5W MW>W     "
4314       "LVLuKU/VLuKU/V?\\?U/V?Y<U/V=X=U&V 2W>X     8X DWBT   ?~Q)~W)~R&~(V4V NW4W EWHW >WBW K~^    "
4315       "           $X   ,X   &VBV Kg \"VEc8WFZ=W  /W !W +T 4~W      5V 1X4X >X (Y -] IW>X )Y M[9X 9"
4316       "X >\\F\\ H[C`    'a    Ca$Y 9UAV:WAU;W<W LX<\\!X *X*X,X -X 0X6f+X/X'X -X -XN[ :X -XEVHVEX0XCX"
4317       "EX)X%X.s KX%X.o 6h <X <X/X!X@X GWDVCVDW*_ 4X -Z >W )V $W       6i JX5X$X -X5X V2W LW 1W3W "
4318       "MW6W MW ,W ,WLY 9W ,W7W7W=W6W!X4X NX5X$X5X MW .[ .W ,W6W KW>W FWEVJVEW#a >W?X 8Z 4\\ 8V  K["
4319       " =i<V  0S=Y=S0X:[ KW@^ IfMW HW4W MY .W6W JW@W =XKX CW6W W#W2WCWCW!W=W JWCWCW\"X4X%X4X ?W >W"
4320       "2W IW<W :Y @X 0X%X1X?X?X-X0X&XBXBX-X%X1~` GV H~` GV H~` GV   DX 3XAS ?r DV8V =\\ <V 2V;X   "
4321       "DSFV'S4W /XFX @~X  .S@VIX;S    (V Ii 8Z   5W6W D_GU IX   4g 3Y .XFX HgGV;TNU<gGVFQ@W;Z=V;T"
4322       "NU3Y 1W<W GW<W GW<W GW<W GW<W GW<W GX>X X *X -X -X -X -X -X -X -X ,X*X-XCXEX)X%X1X%X1X%X1X"
4323       "%X1X%X H_ LX@W<X.X/X'X/X'X/X'X/X GX <X7X NWD\\ 8i >i >i >i >i >i >i3WBX ,V2W!V2W!V2W!V2W NW"
4324       " ,W ,W ,W .W4W MW6W!X4X\"X4X\"X4X\"X4X\"X4X M~Y2X@VIW NW6W MW6W MW6W MW6W KW?X GX5X NW?X     L"
4325       "VLuKU/VLuKU/V@^@U/V@Y;U/V=X=U&V 2X?W     8X CWBT   ?~R*~X)~Q%}(V4W W4W FXHX ?XDX K~^      "
4326       "         $X   ,X   'WCV Ii &VEe:XEZ>W  /W !W +T 4~W      5V 1X4X >X )Y )[ KW=X (Y N[9Y ;Y "
4327       "?Z@Z I]Gb    '^    =^$X 9U@V:WAU<X<X MX9Z\"X *X*X,X -X 0X6f+X/X'X -X -XM[ ;X -XEVHVEX0XBWEX"
4328       ")X%X.r JX%X.q 4e =X <X/X!X@X GXFVAVFX*` 5X .Z =W )V $W       :m JW3W$W ,W3W!W2W LW 1W3W MW"
4329       "6W MW ,W ,WMY 8W ,W7W7W=W6W!W2W NW3W$W3W MW -^ 2W ,W6W KX@X FWEVJVEW\"_ <W@W 7Y :b 7V  Jb F"
4330       "mAX  0S<W<S0W8Y JW<[ KYHVMV GV3X MZ 0W6W IVAX >XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W3X ?W >W"
4331       "2W JW;X <Y ?X 0X&Y1X?X?X-X0X&YCXCY-X%X2~a GV H~a HV I~b HV   DX 3W@S ?r DV8V <Z ;V 2W;W   "
4332       "DSFV'S  <XFX  =V  .S@VGW<S    (V      \"W6W A\\GU IX       2XFX *V;TMU LV2V V;TMU4Z 2X<X IX<"
4333       "X IX<X IX<X IX<X IX<X IX=X X *X -X -X -X -X -X -X -X ,X*X-XBWEX)X%X1X%X1X%X1X%X1X%X G] KX@"
4334       "V;X.X/X'X/X'X/X'X/X GX <X8Y NWC\\ =m Bm Bm Bm Bm Bm Bm3WBW ,W2W\"W2W\"W2W\"W2W NW ,W ,W ,W /X4"
4335       "X NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2W@VHW NW6W MW6W MW6W MW6W JW@W FW3W MW@W     KVLuKU/VLuKU/V"
4336       "A`AU/VAY:U/V=X=U&V 1W@X     9X BWBS   >~R+~Z*~P#{'V4W W4W FXHX ?XDX K~^               $X  "
4337       " ,X   'VBV Gi (VFg;WCZ?W  /W !W +T 4~W      6W 1X4X >X *Y &Z LW=X (Y NZ7X ;X ?Z>Z ImNX    "
4338       "'[    8\\%Y 9UAW:WAU<W:W MX7Y#X *X*X,X -X 0X6f+X/X'X -X -XL[ <X -XEWJWEX0XBXFX)X%X.p HX%X.r"
4339       " 0a >X <X/X XBX FXFVAVFX+b 6X /Z <W )W %W       =p JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,WNY 7"
4340       "W ,W7W7W=W6W!W2W NW3W$W3W MW -b 6W ,W6W JW@W EWFVHVFW!] ;WAX 8Y 9` 5V  H` HrG[  0S<W<S0W8Y"
4341       " JW:Y KXF^ HW2W Kc ;W6W IVAX >XIW CW6W!W!W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W JW:W =Y >X 0Y'"
4342       "X0X?X?X-X0X%XCXCX,X%X2~a GV H~a HV I~b HV   DX 3W@S ?r DV8V <Z   FW;W   DSFV'S  =XFX  <V  "
4343       ".S@VFW=S    (V      \"W6W <WGU IX       1XFX +V;SLU LV2V V;SLU5Z 1W:W IW:W IW:W IW:W IW:W I"
4344       "X<X IX=X X *X -X -X -X -X -X -X -X ,X*X-XBXFX)X%X1X%X1X%X1X%X1X%X F[ JXAW;X.X/X'X/X'X/X'X/"
4345       "X GX <X8X MWB] Bp Ep Ep Ep Ep Ep E~eBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W "
4346       "M~Y2WAWHW NW6W MW6W MW6W MW6W JWAX FW3W MWAX     KV<V=V/V#V/VBbCV/VBY:V/V=X>V&V 1XAW     9"
4347       "X @WDT   ?~S+~Z)}!y'W4W W4W FWFW >WDW J~^               *r   ?V   &VBV Eh *VEXIX<XBZ@W  /W"
4348       " !W +T 4~W  5f   8V 0X4X >X +Y $Z NW<X 'X NZ7X ;X ?X:X HkMX    '[    7[%X 8UAV8VAU=X:X NX6"
4349       "X#X *X*X,X -X 0X6f+X/X'X -X -XK[ =X -XDVJVDX0XAWFX)X%X.m EX%X.XA\\ -^ ?X <X/X XBX FXFVAVFX,"
4350       "c 6X /Y ;W (V %W       ?r JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,a 6W ,W7W7W=W6W!W2W NW3W$W3W M"
4351       "W ,e :W ,W6W JW@W DWGVHVGW N[ 9WBW 8Y 8^ 3V  F^ I~X  0S;U;T1W8Y JW8X MXC\\ HW2W Ia ;W6W IWB"
4352       "W >XHX DW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W >W2W KX:X ?Y =X /X'X0Y@X@Y-X0X%YDXDY,X%X2~a "
4353       "GV H~a HV I~b HV   DX 3W@S ?r DV8V ;X   DW;V   DSFV'S  >XFX  ;V  .S@VFW=S    (V      \"W6W "
4354       ":UGU IX       0XFX -V;TLU MV0U!V;TLU6Y 0X:X KX:X KX:X KX:X KX:X KX:X JW<X X *X -X -X -X -X"
4355       " -X -X -X ,X*X-XAWFX)X%X1X%X1X%X1X%X1X%X F[ JXBW:X.X/X'X/X'X/X'X/X GX <X9Y MWA] Er Gr Gr G"
4356       "r Gr Gr G~gBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WBWGW NW6W MW6W MW6W "
4357       "MW6W IWBW EW3W LWBW     IU<V=V.U#V.UCdDV.UCY9V.U=X>V&V 1XBX     :X ?WDT   ?~S,~[({ x&W4W W"
4358       "4W FWFX ?XFX JV                \"q   >V   &VBV Af -VEXGX=W@ZBW  .W !W +T 4~W  5f   8V 0X4X "
4359       ">X ,Y \"Y W;X 'X NZ7X <Y @Y:Y HiLX    '^    =^%X 8UAV8VAU=X:X NX5X$X *X*X,X -X 0X(X+X/X'X -"
4360       "X -XJ[ >X -XDVJVDX0XAXGX)X%X.i AX%X.X>Z ,\\ ?X <X/X NWBW DWFVAVFW+XMY 7X 0Z ;W (V %W       "
4361       "@s JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,` 5W ,W7W7W=W6W!W2W NW3W$W3W MW +g =W ,W6W JXBX DWGVH"
4362       "VGW N[ 9WBW 9Y 7^ 3V  F^ I[Gr  /S;U;T1W8X IW7X NWA[ HW2W F^ ;W6W HVCX >XGW DW6W!W<W<W3WCWC"
4363       "W!W=W JWCWCW\"W2W%W2W ?W >W2W KW9X ?Y =X /X'X/X@X@X,X0X$YEXEY+X%X2~a GV H~a HV I~b HV   DX "
4364       "3W@S 6X 3V8V ;X   DX<V   DTFV)T  >WEW  :V  .TAVEW?T    (V      \"W6W :UGU IX       /WEW .V;"
4365       "TKU NV/U\"V;TKU7Y /X:X KX:X KX:X KX:X KX:X KX:X KX<X X *X -X -X -X -X -X -X -X ,X*X-XAXGX)X"
4366       "%X1X%X1X%X1X%X1X%X G] KXCW9X.X/X'X/X'X/X'X/X GX <X9Y MW?] Hs Hs Hs Hs Hs Hs H~hBW ,|\"|\"|\"|"
4367       " NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W M~Y2WBVFW NW6W MW6W MW6W MW6W IWBW EW3W LWBW   "
4368       "  IU<V=V.U#V.UDYMZEV.UDY8V.U#V&V 0WBX     ;X >WDS   >~T-~\\(y Mw&W4W W4W GXFX ?XFX JV      "
4369       "          #r   >V   'WCV <c .VEWEW=W?ZCW  .W !W   :~W  5f   9W 0X4X >X -Y  Y!W;X 'Y Y5X =X"
4370       " @Y8Y HgKX    'a    Ca%X 8UAV8VAU=W8W NX4X%X *X+Y,X -X 0X(X+X/X'X -X -XI[ ?X -XDWLWDX0X@WG"
4371       "X)X&Y.X 0X&Y.X=Y *[ @X <X/X NXDX DXHW@VHX,YMZ 8X 1Z :W (W &W       At JW3W$W ,W3W!| LW 1W3"
4372       "W MW6W MW ,W ,` 5W ,W7W7W=W6W!W2W NW3W$W3W MW )g ?W ,W6W IWBW CWGVHVGW MY 8WCX :Y 6` 5V  H"
4373       "` IW@m  -S;V<T1W8X IW7X W@[ HW2W Ia ;W6W HVCW >XFX EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W "
4374       ">W2W KW8W @Y <X /X'X/X@X@X,X0X#YFXFY*X&Y2~a GV H~a HV I~b HV   DX 3W@S 6X 3V8V ;X   CX=V  "
4375       " CSFV)S  =WEW  :V  -SAVDW@S    'V      \"W6W :UGU IX       /WEW .V<TJU NV/U\"V<TJU8Z /W8W KW"
4376       "8W KW8W KW8W KW8W KX:X KX<X X *X -X -X -X -X -X -X -X ,X+Y-X@WGX)X&Y1X&Y1X&Y1X&Y1X&Y H_ LX"
4377       "DW9Y.X/X'X/X'X/X'X/X GX <X:Y LW>] Jt It It It It It I~iBW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W"
4378       "2W\"W2W\"W2W\"W2W\"W2W M~Y2WCVEW NW6W MW6W MW6W MW6W IWCX EW3W LWCX     IV=V=V.V$V.VFYKZFV.VFY"
4379       "7V.V$V&V 0XCW     ;Y =WFT   >~T-~\\'w Ku%W4W W4W GXEW >WFW IV                #q   =V   6~X "
4380       "JSN^ /VEWCW?W=ZDW  .W !W   :~W  5f   9V /X4X >X .Y  MX\"W:X &X Y5X >Y @X6X FcJX    &d    Id"
4381       "%X 8UAV8VAU>X8X X4X$X +X+X+X -X /X)X+X/X'X -X -XH[ @X -XCVLVCX0X@XHX(X'X-X /X'X-X<Y *Z @X "
4382       "<X/X NXDX DXHV?VHX-YKY 8X 2Z 9W 'V &W       B]?W JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,a 6W ,W"
4383       "7W7W=W6W!W2W NW3W$W3W MW 'g AW ,W6W IWBW CWHVFVHW NZ 7WDW :Z 6a 6V  Jb IU;i  ,S;V<S0W7W IW"
4384       "6W W?Z HW2W Kc ;W6W HWEX >XFX EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =V2V KX8X BY ;X /Y)Y/"
4385       "X@X@X,X0X#YFXGZ)X'X0~` GV H~` GV H~` GV   DX 3W@S 6X 3V8V M|  &Z?V   CSFV)S:m AXFX  ;V  -S"
4386       "AVDW@S    'V      \"W6W :UGU      *m 5XFX /V;SIU V.T\"V;SIU9Z /X8X MX8X MX8X MX8X MX8X MX8X "
4387       "MX;X NX +X -X -X -X -X -X -X -X ,X+X,X@XHX(X'X/X'X/X'X/X'X/X'X Ha LXFW8X-X/X'X/X'X/X'X/X G"
4388       "X <X;Z LW<\\ L]?W J]?W J]?W J]?W J]?W J]?W J]?{BW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2"
4389       "W\"W2W\"W2W M~Y2WDVDW NW6W MW6W MW6W MW6W HWDW DW3W KWDW     HV=V>V-V%V-VGYIZHV-VGY7V-V%V%V "
4390       "/WDX     ;X <WFT   >~T-~\\'v Is$W4W W4W GWDX ?XGW HV                %r   =V   6~X JSJ[ 0VEV"
4391       "AV?W<ZFW  -W !W   \"V   Lf   9V /X5X =X /Z  MX\"V9X &X NX5X >X ?X6X D`IX    $d    Ne#X 8UAV8"
4392       "VBU=x X4X$X +X+X+X -X /X)X+X/X'X -X -XG[ AX -XCVLVCX0X?WHX(X'X-X /X'X-X;Y *Y @X <X/X MXFX "
4393       "CXHV?VHX-XIY 9X 3Z 8W 'V &W       CZ;W JW3W$W ,W3W!| LW 1W3W MW6W MW ,W ,b 7W ,W7W7W=W6W!W"
4394       "2W NW3W$W3W MW %f BW ,W6W IXDX BWIVFVIW N\\ 8WEX :Y .[ 7V  K\\ BT8e  *S<X=S0W7V HW6X\"W=X GW2"
4395       "W Me ;W6W GVEX >WDW EW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W KW6W CY :X .X)X.YAXAY,X0X\""
4396       "ZHXHZ(X'X/Y  AV  BY FV GY%Y FV   DX 3W@S 6X 2V:V L|  %ZAV   BSEV*S:m @XFX  <V  -SAVCWAS   "
4397       " 'V      \"W6W :UGU      *m 6XFX .V<TIU V/U\"V<TIU9Y .x Mx Mx Mx Mx Mx Mu NX +X -X -X -X -X "
4398       "-X -X -X ,X+X,X?WHX(X'X/X'X/X'X/X'X/X'X Ic MXGW7X-X/X'X/X'X/X'X/X GX <X=[ KW:[ NZ;W KZ;W K"
4399       "Z;W KZ;W KZ;W KZ;W KZ;{BW ,|\"|\"|\"| NW ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W  &WEVCW NW6W "
4400       "MW6W MW6W MW6W HWEX DW3W KWEX     GV>V>V,V&V,VIYGZIV,VIY6V,V&V&W /XEW     N~X'VGT   =~T-~\\"
4401       "&u Ir#W4W NV4W HXDX ?XHX HV                 KX   ,V   6~X JSHZ 2VDVAV?W;ZGW  -W !W   \"V   "
4402       "Lf   :W .X6X =X 0Z  LY#~ /X NX5X >X @X5Y AYFX    !d >~X >d X 8UAV8VBU>z!X3X%X +X+X+X -X /X"
4403       ")X+X/X'X -X -XF[ BX -XCWNWCX0X?XIX(X'X-X /X'X-X:X )Y AX <X/X MXFX BWHV?VHW-YIY 9X 3Y 7W 'W"
4404       " 'W       CX9W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WNZ 8W ,W7W7W=W6W!W2W NW3W$W3W MW !c CW ,"
4405       "W6W HWDW AWIVFVIW N] 8WFW :Y *Y 8V  KY ?R3`  (S<X=S0W7V HW5W\"W=X GW2W N[ 0W6W GWFW >XDX FW"
4406       "6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W LX6X DY :X .X)X-XAXAX+X0X!ZIXIZ'X'X.Y  BV  CY EV"
4407       " FY'Y EV   DX 3W@S 6X 2V:V L|  $[CV   BTFW,T:m ?XFX  =V  -TBVBVBT    'V      \"W6W :UGU    "
4408       "  *m 7XFX .V<THU!V/U\"V<THU:Y .z z z z z Nx Nv NX +X -X -X -X -X -X -X -X ,X+X,X?XIX(X'X/X'"
4409       "X/X'X/X'X/X'X Je NXGV6X-X/X'X/X'X/X'X/X GX <X@^ KW9[ X9W KX9W KX9W KX9W KX9W KX9W KX9W MW "
4410       ",W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W  &WFVBW NW6W MW6W MW6W MW6W GWFW CW3"
4411       "W JWFW     FV>V?W,V'W,VJYEZKW,VJY6W,V'W&W /XFX     N~X'WHT   =~T-~\\%s Gp\"W4W NV4V GXCW >WH"
4412       "X HW                 LX   ,V   6~X JSGY 3VDWAW@W:ZIW  ,W !W   \"V   Lf   :W .X6X =X 1Z  JX#"
4413       "~ /X NX5X ?Y @X4X .X     Md A~X Ad LX 8UAV8VBU>z!X3X%X +X+X+X -X /X)X+X/X'X -X -XE[ CX -XB"
4414       "VNVBX0X>WIX(X'X-X /X'X-X9X *Y AX <X/X MXFX BXJW?WJX.YGY :X 4Z 7W 'W 'W       DX8W JW3W$W ,"
4415       "W3W!W 'W 1W3W MW6W MW ,W ,WLY 9W ,W7W7W=W6W!W2W NW3W$W3W MW  K_ DW ,W6W HXFX AWIVFVIW ^ 8W"
4416       "FW ;Y (Y 9V  LY >Q.X  $T>Z?T0W8W HW5W\"W<W GW2W Y -W6W GWGX >WCX FW6W!W<W<W3WCWCW!W=W JWCWC"
4417       "W\"W2W%W2W ?W =W4W LX6X EY 9X .Y+Y-YBXBY+X0X ZJXJZ&X'X-Y  CV  DY DV EY)Y DV   DX 3W@S 6X 2W"
4418       "<W L|  #\\FW   ASFW,S9m >XFX  >V  ,SBVBWCS    &V      \"W6W :UGU      *m 8XFX .V<TGU\"V.U#V<T"
4419       "GU;Y -z z z z z z v NX +X -X -X -X -X -X -X -X ,X+X,X>WIX(X'X/X'X/X'X/X'X/X'X KZMZ XHW6X-X"
4420       "/X'X/X'X/X'X/X GX <u JW7Y!X8W LX8W LX8W LX8W LX8W LX8W LX8W MW ,W ,W ,W ,W )W ,W ,W ,W /W2"
4421       "W NW6W!W2W\"W2W\"W2W\"W2W\"W2W  &WGWBW NW6W MW6W MW6W MW6W GWFW CW3W JWFW     FW?V?V+W(V+WKXCY"
4422       "KV+WKX5V+W(V%W .WFX     N~X'WHT   =~T-~\\$q Eo\"W4W NV4V GWBW >XIW GW                 LX    "
4423       "   ;~X JSFX 3VDV?V@W9ZJW  +V \"W   !V       V -X6X =X 2Z  IX#~ /X NX5X ?X ?X4X .X     Jd D~"
4424       "X Dd IX 8UAV8VCV>z!X3X%Y ,X,Y+X -X /Y*X+X/X'X -X -XD[ DX -XBVNVBX0X>XJX(Y)X,X /Y)X,X9Y *X "
4425       "AX <X/X LXHX AXJV=VJX.XEY ;X 5Z 6W &V 'W       DW7W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WKY "
4426       ":W ,W7W7W=W6W!W2W NW3W$W3W MW  H\\ DW ,W6W GWFW @WJVDVJW!` 9WGX <Y &X 9V  LX =P   (T?\\@T0W8"
4427       "X IW5W\"W<W GW2W X ,W6W FVGW >XBW FW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W LW4W FY 8X -X"
4428       "+X+YCXCY*X0X N\\MXM\\%Y)X+Y  DV  EY NQFVFQ Y+Y CV   DX 3W@S 6X 1V<V K|  ![HW   @TFW.T9m =XFX"
4429       "  ?V  ,TCVAVDT    &V      \"W6W :UGU      *m 9XFX -V<SFU\"V/U\"V<SFU;X ,z z z z z z v NY ,X -"
4430       "X -X -X -X -X -X -X ,X,Y,X>XJX(Y)X.Y)X.Y)X.Y)X.Y)X KZKZ!YJW6X,X/X'X/X'X/X'X/X GX <t IW6Y\"W"
4431       "7W LW7W LW7W LW7W LW7W LW7W LW7W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W "
4432       " &WHWAW NW6W MW6W MW6W MW6W GWGX CW3W JWGX     EV?V@W*V)W*VJVAWKW*VJV5W*V)W%W .XGW     M~X"
4433       "&WJT   <~S,kNn#o Cm!W4W NV4V HXBX ?XJX FW                 MY       <~X JSEX 5VCV?V@W8ZLW  "
4434       "*W #W   !V       V -X6X =X 3Z  HX#~ /X NX5X @Y ?X4X /X     Ge G~X Ge GX 8UAV9WCU>|\"X3X$X ,"
4435       "X,X*X -X .X*X+X/X'X -X -XC[ EX -XA\\AX0X=WJX'X)X,X .X)X,X8X *X AX <X/X LXHX AXJV=VJX/YEY ;X"
4436       " 6Z 5W &V 'W       DW7W JW3W$W ,W3W!W 'W 1W3W MW6W MW ,W ,WJY ;W ,W7W7W=W6W!W2W NW3W$W3W M"
4437       "W  EZ EW ,W6W GWFW ?WKVDVKW!b 9WHW <Y $W 9V  LW     BTAVNUAT/W8X IW5W#W;V FW2W!X +W6W FWIX"
4438       " >XBX GW6W!W<W<W3WCWCW!W=W JWCWCW\"W2W%W2W ?W =W4W MX4X HY 7X -Y-Y+ZDXDZ*X0X Mt#X)X*Y  EV  "
4439       "FY NSGVGS Y-Y MQFVFQ   X 3W@S 6X 1W>W 9X   =\\KW   >SEW<PCS  6XFX  @V  +SCVAWES    %V      "
4440       "\"W6W :UGU        &XFX -V<TFU#V/U\"V<TFU<X ,|\"|\"|\"|\"|\"|\"w MX ,X -X -X -X -X -X -X -X ,X,X+X="
4441       "WJX'X)X-X)X-X)X-X)X-X)X LZIZ!XKW5X,X/X'X/X'X/X'X/X GX <s HW5X\"W7W LW7W LW7W LW7W LW7W LW7W"
4442       " LW7W MW ,W ,W ,W ,W )W ,W ,W ,W /W2W NW6W!W2W\"W2W\"W2W\"W2W\"W2W  &WIW@W NW6W MW6W MW6W MW6W"
4443       " FWHW BW3W IWHW     DW@VAW)W+W)WJT?UKW)WJT5W)W+W$W -WHX     M~X&WJT   ;eMQMe+jNQNj!m Bl W4"
4444       "W NW6W HXBX >WJX FW                 LX       <~X JSEX 6WCV?V@W7ZMW  *W #W   !V      !W -X6"
4445       "X =X 4Z  GX#~ /X NX5X @X >X4X /X     De J~X Je DX 8U@V:WDV>|\"X3X$X ,X-Y*X -X .X*X+X/X'X -X"
4446       " -XB[ FX -XA\\AX0X=XKX'X*Y,X .X*Y,X8Y +X AX <Y1Y KWHW ?WJV=VJW/YCY <X 7Z 4W &W (W       EW6"
4447       "W JX5X$X -X5X!X (W 0W5X MW6W MW ,W ,WIY <W ,W7W7W=W6W!X4X NX5X$X5X MW  CX EW ,W6W GXHX ?WK"
4448       "VDVKW!XNY :WIX =Y #X :V  MX     BUCVMVBT/W9Y IW5W#W<W FW3X!W *W6W EVIX ?X@W GW6W!W=Y=W3XDW"
4449       "DX!W=W JWCWCW\"X4X%X4W >W <W6W LX4X HY 7X ,X-X)ZEXEZ)X0X Lr\"X)X)Y  FV  GY NUHVHU Y/Y MSGVGS"
4450       "  !X 3XAS 6X 0W@W 8X   ;\\NW   =TEX@RDT  5XFY  BV  +TDV@WGT    %V      \"W6W :UGU        (YF"
4451       "X ,V=TEU#V0U!V=TEU<X ,|\"|\"|\"|\"|\"|\"w MX ,X -X -X -X -X -X -X -X ,X-Y+X=XKX'X*Y-X*Y-X*Y-X*Y-"
4452       "X*Y MZGZ\"XLW5Y,Y1Y'Y1Y'Y1Y'Y1Y GX <r GW4X$W6W MW6W MW6W MW6W MW6W MW6W MW6X NX -X -X -X -X"
4453       " *W ,W ,W ,W /W2W NW6W!X4X\"X4X\"X4X\"X4X\"X4X  &WIV@X NW6W MW6W MW6W MW6W FWIX BX5X IWIX     "
4454       "CWAVAW(W,W(WJR=SJW(WJR4W(W,W$W -XIX     M~X&WJS   :dLQLd+iMQNj!l @j NW4W NW6W HW@W >WJW DW"
4455       "                 MX       .VCV :SDW 6VBV?V@W6b  )W #W   !V      !V +X8X <X 5Z  FX#~ /X MW5"
4456       "X @X >X4X /X     Ad L~X Ld AX 8VAV:WDU=|\"X3X$Y -X-Y*X -X .Y+X+X/X'X -X -XA[ GX -XA\\AX0X<WK"
4457       "X'Y+X+X .Y+Y,X7X +X AX ;X1X JXJX ?XLW=WLX/XAY =X 7Y 3W %V (W       EW7X JX5W\"W ,W5X W (W 0"
4458       "W5X MW6W MW ,W ,WHY =W ,W7W7W=W6W W4W MX5W\"W5X MW  BX FW ,W6W FWHW >WKVDVKW\"XLX 9WJW =Z #X"
4459       " :V  MX     AUEVKVDU/X:Y IW5W#W<W EW4W!X *W6W EVJX >X@W GW6W!W=Y=W2WDWDW W=W JWCWCW\"X4W#W4"
4460       "W >W <W6W LW2W IY 6X ,Y/Y(ZFXFZ(X0X Kp!Y+X'Y  GV  HY NWIVIW Y1Y MUHVHU  \"X 2WAS 6X 0YDY 8X"
4461       "   :c   <TE[FUDS  3XFY  CV  *SDV@WGS    $V      \"W6W :UGU        )YFX ,V=TDU$V0V\"V=TDU=X +"
4462       "|\"|\"|\"|\"|\"|#x MY -X -X -X -X -X -X -X -X ,X-Y+X<WKX'Y+X,Y+X,Y+X,Y+X,Y+X MZEZ#YNW4X*X1X%X1X"
4463       "%X1X%X1X FX <p EW4X$W7X MW7X MW7X MW7X MW7X MW7X MW7Y MW ,W ,W ,W ,W *W ,W ,W ,W .W4W MW6W"
4464       " W4W W4W W4W W4W W4W  $WKV?W MW6W MW6W MW6W MW6W EWJW AX5W GWJW     BXBVBW'X.W'XJP;QJW'XJP"
4465       "4W'X.W#V ,XIW     L~X%WLT   :dLQLc*iMQMi k ?i NW4W NW6W IX@X ?XLX DW                 MY   "
4466       "    0VBV :SDW 7VAV?V@X6a  )W #W   !V      !V +X8X <X 6Z  EX#~ 0Y MW5X AY >X4X 0X     =d ~X"
4467       " d   LUAW<XEV>X2X#X3X#X -X.Y)X -X -X+X+X/X'X -X -X@[ HX -X@Z@X0X<XLX&X+X+X -X+X+X7Y ,X AX "
4468       ";X1X JXJX ?XLV;VLX0YAY =X 8Z 3W %V (W       EW7X JX5W\"W ,W5X W (W 0W5X MW6W MW ,W ,WGY >W "
4469       ",W7W7W=W6W W4W MX5W\"W5X MW  BX FW ,W7X FWHW >WLVBVLW#YKX :WJW =Y !W :V  MW     @VHXJWHV-W:"
4470       "Y IW5W#W<W EW4W!W )W6W EWKX ?X?X HW6W!X>Y>W1WDWDW W=W JWCWCW\"X4W#W4W >W <W6W MX2X KY 5X +Y"
4471       "1Y'[GXH\\(X0X Jn NX+X&Y  HV  IY NYJVJY Y3Y MWIVIW  #X 2WAS 6X 0[H[ 8X :V %`   :TEiET  2YGY "
4472       " DV  *TEV?WIT    $V      \"W6W :UGU        *YGY ,V<SCU%V0V\"V<SCU=X ,X2X$X2X$X2X$X2X$X2X$X2X"
4473       "$X8X LX -X -X -X -X -X -X -X -X ,X.Y*X<XLX&X+X+X+X+X+X+X+X+X+X NZCZ#`3X*X1X%X1X%X1X%X1X FX"
4474       " <m BW3W$W7X MW7X MW7X MW7X MW7X MW7X MW7Y MW ,W ,W ,W ,W *W ,W ,W ,W .W4W MW6W W4W W4W W4"
4475       "W W4W W4W 5Z IWLV>W MW7X MW7X MW7X MW7X EWJW AX5W GWJW     AXCVCW%X0W%X0W%X0W%X0W\"V +WJX  "
4476       "   ?X 2WLT   9bKQKb)gLQMh Mi =g MW4W MV6W IX@X ?XLX CW                 MX       0VBV :SDW "
4477       "7VAV?V@X5_  (W #W   !V      \"W +X8X <X 7Z  DX 5X 'X LX7X @X =X4X 0X     ;e   Le   JUAW<XFV"
4478       "=X1W#X3X#Y .X.Y)X -X -Y,X+X/X'X -X -X?[ IX -X@Z@X0X;XMX&Y-Y+X -Y-Y+X6X ,X AX ;X1X IXLX >XL"
4479       "V;VLX1Y?Y >X 9Z 2W %W )W       EW7X JX5W\"X -W5X X )W 0X7Y MW6W MW ,W ,WFY ?W ,W7W7W=W6W W4"
4480       "W MX5W\"W5X MW  AW FW ,W7X FXJX =WMVBVMW#YJY ;WKX >Y  W :V  MW     ?dId,W;Z IW5W#W=W DW4W!W"
4481       " )W6W DVKW >X>W HW6W W>Y>W1WDWDW W=W JWCWDX\"X4W#W4W >W ;V7W LX2X LY 4X *X1X%]JXJ]'X0X Hj L"
4482       "Y-Y%Y  IV  JY LYKVKY MY5Y MYJVJY  $X 2XBS 6X 2q 9X :V #\\   7TDgFT  /XFX  EV  )TFV>VJT    #"
4483       "V      \"W6W :UGU        +XFX *V=TCU%V1V!V=TCU=X ,X1W$X1W$X1W$X1W$X1W$X2X%X7X LY .X -X -X -"
4484       "X -X -X -X -X ,X.Y*X;XMX&Y-Y+Y-Y+Y-Y+Y-Y+Y-Y ZAZ$_3Y*X1X%X1X%X1X%X1X FX <i >W3W$W7X MW7X M"
4485       "W7X MW7X MW7X MW7X MW7Z NX -X -X -X -X +W ,W ,W ,W .W4W MW6W W4W W4W W4W W4W W4W 5Z IWMV=W"
4486       " MW7X MW7X MW7X MW7X EWKX AX5W GWKX     @XDVDX$X2X$X2X$X2X$X2X\"V +XKW     ?X 1WMT   7`JQKa"
4487       "'fLQLf Kg <f LW4W MW8W HW>W >WLW BX                 NY       1VBV :SDW 8V@V?V?W4]  &V $W  "
4488       "  V      \"V *Y:Y <X 8Z  DY 5X 'X KW7X @X =X5Y 1Y     8e  #e   GU@W>YGW>X0X$X4Y\"Y /X/Y(X -X"
4489       " ,Y-X+X/X'X -X -X>[ JX -X@Z@X0X;XMX%Y/Y*X ,Y/Y*X6Y -X AX ;Y3Y IXLX =WLV;VLW0X=Y ?X :Z 1W $"
4490       "V )W       EW8Y JY7X\"X -X7Y X )W 0X7Y MW6W MW ,W ,WEY @W ,W7W7W=W6W X6X MY7X\"X7Y MW  AW FW"
4491       " ,X8X EWJW <WMVBVMW#XHX :WLW >Y  NW :V  MW     >bGc,W;[ JW6X#W=W DX6X!W )W6W DVLX >W=X IW7"
4492       "X W>Y>W1XEWEX W=W IWDWDW!Y6X#X6X >W ;W8W MX0X MY 4X *Y3Y$^LXL^&X0X Ff IY/Y#Y  JV  KY JYLVL"
4493       "Y KY7Y KYKVKY  #X 2XBS 6X 3t ;X :V ![   8TCfFT  .XFX  FV  )UGV>WKT            MW7X :UGU   "
4494       "     ,XFX *V=TBU&V2W!V=TBU=X -X0X&X0X&X0X&X0X&X0X&X0W%X7X KY /X -X -X -X -X -X -X -X ,X/Y)"
4495       "X;XMX%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y Z?Z$^4Y)Y3Y%Y3Y%Y3Y%Y3Y FX <X -W3W$W8Y MW8Y MW8Y MW8Y MW8Y MW8Y "
4496       "MW8[ NX -X -X -X -X +W ,W ,W ,W .X6X MW6W X6X X6X X6X X6X X6X 5Z I_=X MX8X MX8X MX8X MX8X "
4497       "DWLW @Y7X FWLW     >XEVFY\"X5Y\"X5Y\"X5Y\"X5Y!V *WLX     @X /WNT   7`JQJ_&eKQKe Je :d KW4W MW8"
4498       "W HW>X ?XNX AX                 Y       1VCV 9SDW 9V?V?V?X4\\  &W %W    V      \"V )X:X ;X 9Z"
4499       "  CX 4X (Y KW7X AX <Y6Y 1X     4e  )e   DVAX@ZHW=X0X$X4Y\"Y*P&X0Z(X -X ,Y-X+X/X'X -X -X=[ K"
4500       "X -X?X?X0X:XNX%Y/Y*X ,Y/Y*X5X .Y AX :X3X HXLX =XNW;WNX1Y=Y ?X ;Z 0W $V )W       EW8Y JY7W "
4501       "W ,W7Y NX *W /W8Z MW6W MW ,W ,WDY AW ,W7W7W=W6W NW6W LY7W W7Y MW  AW FW ,X9Y EWJW <WMVBVMW"
4502       "$XFX ;WMX ?Y  MW :V  MW     =`Ea+X<[ JW6W\"W>W BW6W W )W6W DWMX ?X=X IX8X W?[?W0WEWEW NW=W "
4503       "IWDWDW!Y6W!W6W =W ;W8W MX0X NY 3X )Y5Y\"z%X0X C` FY/Y\"X  JV  KX HYMVMY IX7X IYLVLY  \"X 1XCS"
4504       " 6X 4v <X :V  [   8TBbET  ,WEW  FV  (T$T            LX8X :UGU        ,WEW )V=m,V3W V=mCX -"
4505       "X0X&X0X&X0X&X0X&X0X&X0X&X7X KY*P&X -X -X -X -X -X -X -X ,X0Z)X:XNX%Y/Y)Y/Y)Y/Y)Y/Y)Y/Y!Z=Z"
4506       "%]3Y(X3X#X3X#X3X#X3X EX <X -W3W$W8Y MW8Y MW8Y MW8Y MW8Y MW8Y MW8[ MW ,X -X -X -X ,W ,W ,W "
4507       ",W -W6W LW6W NW6W MW6W MW6W MW6W MW6W 4Z H^=W LX9Y MX9Y MX9Y MX9Y DWMX @Y7W EWMX     =Y8Y "
4508       "Y8Y Y8Y Y8Y Y8Y V *WLX     AX .WNT   6^IQI]$cKRJc Id 8c KW4W MX:X IX>X ?XNX AY            "
4509       "     Y4P       VBV 9SDW 9V?V?V?Y4Z  %W %W    V      #W )X:X ;X :Z  CY 4X (Y KX9Y AX ;X6X 1"
4510       "Y     1e  /e   @U@XB[JX<X/W$X4X Y,Q&X1Z'X -X +Y.X+X/X'X -X -X<[ LX -X?X?X0X:XNX$Y1Y)X +Y1Y"
4511       ")X5Y /X @X :X4Y GXNX <XNV9VNX2Y;Y @X ;Y /W $W *W       EW9Z JZ9X X -X9Z NX *W /X9Z MW6W MW"
4512       " ,W ,WCY BW ,W7W7W=W6W NX8X LZ9X X9Z MW  AW FW +W9Y EXLX <WNV@VNW%YEX ;WNW ?Y  LW :V  MW  "
4513       "   <^C_)W=\\ JX7W\"W>W BX8X W )W6W CVNX >W;W IX8X X@[@X0XFWEW NW=W IWDWEX!Z8X!X8X =W :W:W LX"
4514       "0X Y 2X (Y7Y Nv#X0X ?X AY1Y V  IV  JV FYNVNY GV5V GYMVMY  !X 1XCS 6X 5x =X :V  MZ   8T?ZBT"
4515       "  *VDV  FV  'T&T            KX8X :UGU        ,VDV )V<m-V3V NV<mCX -X/W&X/W&X/W&X/W&X/W&X0X"
4516       "'X6X JY,Q&X -X -X -X -X -X -X -X ,X1Z(X:XNX$Y1Y'Y1Y'Y1Y'Y1Y'Y1Y!Z;Z%[3Y'X4Y#X4Y#X4Y#X4Y EX"
4517       " <X -W3W$W9Z MW9Z MW9Z MW9Z MW9Z MW9Z MW9] NX -X -X -X -X ,W ,W ,W ,W -X8X LW6W NX8X MX8X "
4518       "MX8X MX8X MX8X 4Z H]=X KW9Y LW9Y LW9Y LW9Y CWNW ?Z9X DWNW     ;Y;Z MY;Z MY;Z MY;Z MY;Z NV "
4519       "*XMW     AY -[   3ZHRH[\"aJRI` Fb 6a JW4W LW:W HX=W >WNX @Y                !Z6Q       VBV K"
4520       "P>SEW 9V>WAW>X3Z  &W %W    V      #V 'X<X :X ;Z  BY 4X )Y IW9X AY ;Y8Y 2Y     .d  1d   >U?"
4521       "ZH^MZ<X.X%X5Y NY.R&X2Z&X -X *Y/X+X/X'X -X -X;[ MX -X&X0X9a$Z3Y(X *Y3Y(X4X$P-Y @X :Y5Y GXNX"
4522       " <XNV9VNX2X9Y AX <Z /W #V *W       EX:Z JZ9X NX .X9Z MX +W .X;[ MW6W MW ,W ,WBY CW ,W7W7W="
4523       "W6W NX9Y LZ9X X9Z MW  AW FW +W:Z DWLW :^@^$XDY <WNW @Z  LW :V  MW     ;\\@['X>\\ JX8X\"W?W AX"
4524       "9Y X *W6W CVNX ?X;X JX9Y NW@[@W/XFWFX NW=W IXEWEX!Z8X!X8W ;W ;W;X MX.X\"Y 1X 'Y9Y Lt\"X0X ?X"
4525       " @Y3Y MT  HV  IT Dj ET3T EYNVNY   X 0XDS 6X 6ZM`LY >X :V  LY   7T)T  (UCU     ET(T        "
4526       "    JX9Y :UGU        ,UCU )V;m.V3V NV;mCY7P HX.X(X.X(X.X(X.X(X.X(X.X(X6X IY.R&X -X -X -X -"
4527       "X -X -X -X ,X2Z'X9a$Z3Y&Z3Y&Z3Y&Z3Y&Z3Y!Z9Z&Z3Y&Y5Y#Y5Y#Y5Y#Y5Y EX <X -W3W$X:Z MX:Z MX:Z M"
4528       "X:Z MX:Z MX:Z MX:^ NX -X -X -X -X -W ,W ,W ,W -X8X LW6W NX9Y MX9Y MX9Y MX9Y MX9Y 4Z H\\=Y K"
4529       "W:Z LW:Z LW:Z LW:Z CWNW ?Z9X DWNW     :[@[ K[@[ K[@[ K[@[ K[@[ MV )WNX     AX ,[   1WGRFW "
4530       "N_IRH^ Da 5_ IW4W LX<X HW<W >` >Y                !Y8S   MX   +VBV KQ?SFX 9V=VAV=Y6]  &V &W"
4531       "    NV BX   1X 1V 'Y>Y :X <Z  BY 3X GP3Z IX;Y AX :Y9Z 2X GX -X  7a  1a .X 6V@iNa;X.X%X6Z N"
4532       "Z1T&X4\\&X -X *Z0X+X/X'X -X -X:[ NX -X&X0X9a#Z5Z(X *Z5Z(X4Y%R/Y @X 9Y7Y EWNW :WNV9VNW2Y9Y A"
4533       "X =Z .W #V *W       EX;[ J[;X MY .X;[ MY2P JW .Y=\\ MW6W MW ,W ,WAY DW ,W7W7W=W6W MX:X K[;X"
4534       " MX;[ MW /P4X FX ,X<[ DXNX :^@^%XBX <` @Y  KW :V  MW     8V;W%X?^ KY9X!V@X @X:X NX *W6W C_"
4535       " >X:W JY;Z NXB]BX.XGWGX MW=W HXFWFX [:X NX:X ;W :W<W LX.X\"Y 1X &Y;Y Ip X0X ?X @Z5Z LR  GV "
4536       " HR Bh CR1R Cj   NX 0YES 6X 7ZJ\\IY ?X :V  KY   8U+U  'TBT     DU+T            IY;Z :UGU   "
4537       "     ,TBT (V;m.V4V MV;mCY8Q HX.X(X.X(X.X(X.X(X.X(X.X)X5X IZ1T&X -X -X -X -X -X -X -X ,X4\\'"
4538       "X9a#Z5Z%Z5Z%Z5Z%Z5Z%Z5Z\"Z7Z&Z5Z%Y7Y!Y7Y!Y7Y!Y7Y DX <X -W4X$X;[ MX;[ MX;[ MX;[ MX;[ MX;[ MX"
4539       ";`3P=Y .Y2P LY2P LY2P LY2P LW ,W ,W ,W ,X:X KW6W MX:X KX:X KX:X KX:X KX:X 3Z GZ<X JX<[ LX<"
4540       "[ LX<[ LX<[ C` ?[;X C`     9_J_ I_J_ I_J_ I_J_ I_J_ LV )`     AX +Z    S <[GRFZ A_ 4^ HW4W"
4541       " KX>X HX<X ?` =Z                \"Y:T   MX   +VCV JSASFX :V<VAV<Y8_  'W 'W    NV BX   1X 2W"
4542       " &X>X 9X =Z 1P2Z 3X GQ5Z GX=Y @X 9Y:Y KP8Z GX -X  4^  1^ +X 5U?gM_9W,W%X7Z L[4U&X6]%X -X )"
4543       "[2X+X/X'X -X -X9[ X -X&X0X8`\"Z7Z'X )Z7Z'X3X%T2Y ?X 9Z9Z E` :_9_3Y7Y BX >Z -W #W +W       D"
4544       "X=\\ J\\=Y LY7P HY=\\ LY5R JW -Y?] MW6W MW ,W ,W@Y EW ,W7W7W=W6W MY<Y K\\=Y MY=\\ MW /R6W DW ,Y"
4545       "=[ CWNW 9^@^&X@X <^ @Y  JW :V  MW       HXA` LZ;X V@W ?Y<Y MX +W6W B^ ?X9W JZ<Z NXB]BX.YHW"
4546       "HY MW=W HYGWGY \\<Y NY<X :W :X>X LX.X#Y 0X %Y=Z Gl MX0X ?X ?Z7Z JP  FV  GP @f AP/P Ah   MX "
4547       "/YFSDP BX 8ZFVEY @X :V  JX   7V.U  %SAS     CU.U            HZ<Z :UGU        ,SAS (V:m/V5W"
4548       " MV:mBY;S HW,W(W,W(W,W(W,W(W,W(X.X)X5X H[4U&X -X -X -X -X -X -X -X ,X6]&X8`\"Z7Z#Z7Z#Z7Z#Z7"
4549       "Z#Z7Z\"Z5Z&[8Z$Z9Z!Z9Z!Z9Z!Z9Z DX <X -W4W\"X=\\ LX=\\ LX=\\ LX=\\ LX=\\ LX=\\ LX=b6R<Y7P GY5R KY5R"
4550       " KY5R KY5R LW ,W ,W ,W ,Y<Y KW6W MY<Y KY<Y KY<Y KY<Y KY<Y 3Z GY<Y JY=[ LY=[ LY=[ LY=[ B^ >"
4551       "\\=Y B^     7r Gr Gr Gr Gr KV (_     BX )Y    S 8RBSCR <] 2\\ GW4W KZBZ HX;W >_ <[          "
4552       "      $[=U   MX   ,VBV JUCSHY :V;WCW<[<b  (W 'W    NV BX   1X 2W &Y@Y 9X >Z 0R5Z 2X GT9[ G"
4553       "Y?Z AY 9[>[ KR;Z FX -X  1[  1[ (X 5V>dL^9X,X&X9[ J[7W&X9_$X -X (\\6Z+X/X'X -X -X8[!X -X&X0X"
4554       "8`![;[&X ([;[&X3Y&W7[ ?X 8Z;Z D` :^7^3X5Y CX ?Z ,W #W +W       DY?] J]?Y KZ:R GY?] LZ8T JW"
4555       " -ZA^ MW6W MW ,W ,W?Y FW ,W7W7W=W6W LY>Y J]?Y KY?] MW /T9X DX ,Y@] CWNW 9]>]'Y@Y =^ AY  IW"
4556       " :V  MW       HYCXNW L\\>Y VAX >Y>Y LY ,W6W B] >X9X K[>[ MXDVMVDX,YIWIY LW=W GYHWHY N]>Y LY"
4557       ">Y :X :X@X LX,X%Y /X $ZAZ Ch KX0X ?X >[;[   ?V   6d   >f   LX /[HSFR BX 9Z3Y AX :V  IX   7"
4558       "V1V  #R@R     BU0U            G[>[ :UGU        ,R@R 'V(U)V6W LV(U<Z>U IX,X*X,X*X,X*X,X*X,X"
4559       "*X,X*W4X G[7W&X -X -X -X -X -X -X -X ,X9_%X8`![;[![;[![;[![;[![;[\"Z3Z(];[\"Z;Z NZ;Z NZ;Z NZ"
4560       ";Z CX <X -WJP;X\"Y?] LY?] LY?] LY?] LY?] LY?] LY?XNZ9T<Z:R GZ8T KZ8T KZ8T KZ8T LW ,W ,W ,W "
4561       "+Y>Y JW6W LY>Y IY>Y IY>Y IY>Y IY>Y 2Z FY>Y HY@] KY@] KY@] KY@] B^ >]?Y A^     6o Do Do Do "
4562       "Do IV (_     CX (Y    S (S ,[ 0[ GW4W J\\H\\ GW:W >^ :\\                %[@W   MX   ,VBV JXFS"
4563       "IZ :V:WEW:\\@e  (V 'V    MV BX   1X 2V $ZDZ 8X ?Z /U;] 2X GV=\\ EZC[ @X 7[@[ JT?[ EX -X  /Y "
4564       " 1Y &X 5V=bK\\7X,X&X<^ I]=Z&X=b#X -X ']:\\+X/X'X -X -X7[\"X -X&X0X7_ \\?\\%X '\\?\\%X2X&Z<\\ >X 7["
4565       "?[ B^ 9^7^4Y5Y CX ?Y +W \"V +W       DZB_ J_CZ I[>T G[C_ K[=W JW ,\\GXNW MW6W MW ,W ,W>Y GW "
4566       ",W7W7W=W6W KZBZ I_CZ J[C_ MW /W>Z DZ .ZB^ C` 8\\>\\&X>Y =\\ AY  HW :V  MW       GZFYNY N]AZ N"
4567       "WCX <ZBZ JZ:Q EW6W B] ?X7W K\\A^ NYFWMWFY,ZJWJY KW=X H[JWJ[ N_BZ JZBZ 8Y <ZDZ LX,X&Y .X #ZC"
4568       "Z >_ FX0X ?X =\\?\\   >V   5b   <d   KX .\\JSHT BX 8X2X @X :V  IX   5V4U   Q?Q     AV4V      "
4569       "      F\\A^ ;UGU        ,Q?Q 'V'U*V6W LV'U<[AW IX,X*X,X*X,X*X,X*X,X*X,X+X4X F]=Z&X -X -X -X"
4570       " -X -X -X -X ,X=b$X7_ \\?\\ N\\?\\ N\\?\\ N\\?\\ N\\?\\ X1X(`?\\ [?[ L[?[ L[?[ L[?[ BX <X -WJS@Z\"ZB_ "
4571       "LZB_ LZB_ LZB_ LZB_ LZB_ LZBYM\\>W;[>T F[=W J[=W J[=W J[=W LW ,W ,W ,W *ZBZ IW6W KZBZ GZBZ "
4572       "GZBZ GZBZ GZBZ 1Z F[BZ GZB^ KZB^ KZB^ KZB^ A\\ =_CZ ?\\     3l Al Al Al Al HV (^     BX (X  "
4573       "  NS (S ,Z .Y FW4W In GX:X ?^ 9_                (]FZ   MX   ,VBV J[ISL\\ :V9XGX9^Fi  )W )W "
4574       "   MV BX   1X 3W #[H[ Et Mx MZC_ 1X GZD^ C[G\\ @Y 7^F] IXF] DX -X  ,V  1V #X 4V<^IY5X*X'y G"
4575       "_D^&{!y NX &`B`+X/X'X -X -X6[#w LX&X0X7_ N^E^$X &^E^$X2Y'^C^ =X 7^E^ B^ 8]7]4Y3Y DX @~U&W "
4576       "\"W ,W       C\\HYNW JWNXG\\ H]EX F\\GXNW J]D[ JW +kMW MW6W MW ,W ,W=Y HW ,W7W7W=W6W K]H] IWNX"
4577       "G\\ I\\GXNW MW /[E\\ Be 9[GXNW B^ 7\\>\\'X<X =\\ AX  GW :V  MW       G\\IYM^$`F\\ MWEX ;]H] J]BV E"
4578       "W6W A\\ ?X7X L_GaKP#ZJYMYJZ*[LWL[ KW=Y H\\LWL\\ MWNXG] J]H\\ 7a C[H[ L~W'x MX 1iEi HX CX0X ?X "
4579       "<^E^   =V   4`   :b   JX -^MSLX Lz V0V ?X :V  HW   4V7V   MP>P     @W8W    3~W      :_GaKP"
4580       " @UGU        ,P>P 'V&U+V6V KV&U;]GZ JX*X,X*X,X*X,X*X,X*X,Y,Y,X4y7_D^&y Ny Ny Ny NX -X -X -"
4581       "X ,{\"X7_ N^E^ L^E^ L^E^ L^E^ L^E^ MV/V(dE^ N^E^ L^E^ L^E^ L^E^ BX <X -WJWF[ \\HYNW K\\HYNW K"
4582       "\\HYNW K\\HYNW K\\HYNW K\\HYNW K\\H[K^E[:]EX E]D[ I]D[ I]D[ I]D[ LW ,W ,W ,W )[F[ HW6W K]H] G]H"
4583       "] G]H] G]H] G]H] 1Z F]G] F[GXNW J[GXNW J[GXNW J[GXNW A\\ =WNXG\\ ?\\     1h =h =h =h =h FV ']"
4584       "     AV &W    T )T +X -X EW4W Hl FX9W ?^ 8~R                Jp   MX   ,VCV It 9V8XIX7sLZ  "
4585       "*W )W    MV BX   1X 3W #n Et Mx Mu 0X Gs Ao @X 5t In CX -X  )S  1S  X 4V9XFU1X*X'x Ex&z y "
4586       "NX %|*X/X'X -X -X5[$w LX&X0X6^ Mu#X %u#X1X'y =X 6u A^ 8]7]4X1X DX @~U&W \"W ,W       ClMW J"
4587       "WMk Fo EkMW Is JW *jMW MW6W MW ,W ,W<Y IW ,W7W7W=W6W Jp HWMk GkMW MW /q Ae 9kMW B^ 7\\=[(Y;"
4588       "X >\\ Av 6W :V  MW       FkL]$u LXGX 9p Hp EW6W A[ ?X6X LpN\\#hKh)s JW<] Lu LWNm Hp 6` Bl K~"
4589       "W'x MX 1iEi HX CX0X ?X ;u   <V   3^   8`   IX ,o Lz NT.T >X :V  HW   3X=X        )X<X    2"
4590       "~W      :pN\\ @UGU           V&U+V7i.V&U:o JX*X,X*X,X*X,X*X,X*X,X*X-X3y6x&y Ny Ny Ny NX -X "
4591       "-X -X ,z!X6^ Mu Ju Ju Ju Ju KT-T(} Lu Ju Ju Ju AX <X -WJk NlMW KlMW KlMW KlMW KlMW KlMW Kn"
4592       "Is9o Ds Hs Hs Hs LW ,W ,W ,W )p HW6W Jp Ep Ep Ep Ep   Ls EkMW JkMW JkMW JkMW A\\ =WMk >\\   "
4593       "  /c 8c 8c 8c 8c CV '\\     ?T %W    U *T *W ,V DW4W Gj EW8W >\\ 5~P                In   LX "
4594       "  -VBV Is 9V7g6qJZ  *V )V    LV BX   1X 3V !l Dt Mx Mt /X Gr ?m ?X 4r Hm BX -X  &P  1P  LX"
4595       " 3V 3X*X'w Cv%x My NX #x(X/X'X -X -X4[%w LX&X0X5] Ls\"X $s\"X1Y(w ;X 5s ?\\ 7\\5\\5Y1Y EX @~U&W"
4596       " !V ,W       BjLW JWMj Dn DjMW Hr JW )hLW MW6W MW ,W ,W;Y JW ,W7W7W=W6W In GWMj EjMW MW /p"
4597       " ?d 8iLW B^ 6Z<[)Y:Y >Z @v 6W :V  MW       EiK]$t JYLZ 7n Fo EW6W A[ ?X5W LWNfM\\\"gKg'q IW<"
4598       "] Ks KWMk Fn 5` Aj J~W'x MX 1iEi HX CX0X ?X :s   ;V   2\\   6^   HX +n Lz MR,R =X :V  HW   "
4599       "1ZEZ        %ZDZ    0~W      :WNfM\\ @UGU          !V%U,V6i/V%U9n JX*X,X*X,X*X,X*X,X*X,X*X-"
4600       "X3y5v%y Ny Ny Ny NX -X -X -X ,x NX5] Ls Hs Hs Hs Hs IR+R(WMs Js Hs Hs Hs @X <X -WJk MjLW J"
4601       "jLW JjLW JjLW JjLW JjLW JmHr8n Cr Gr Gr Gr LW ,W ,W ,W (n GW6W In Cn Cn Cn Cn   Ls CiLW Ii"
4602       "LW IiLW IiLW @Z <WMj <Z     +] 2] 2] 2] 2] @V &[     >R $V    NU *U *U *U DW4W Fh DW8X ?\\ "
4603       "4~                Hl   KX   -VBV Hp 8V5e4nGZ  +W +W    LV BX   1X 3V  j Ct Mx Mr -X Gq =j "
4604       ">Y 3p Gl AX -X       2X 3W 5X(X(u ?s$v Ky NX \"v'X/X'X -X -X3[&w LX&X0X5] Kq!X #p X0X(v :X "
4605       "4p =\\ 7\\5\\6Y/Y FX @~U&W !V ,W       AhKW JWLh Bm ChLW Gq JW (eJW MW6W MW ,W ,W:Y KW ,W7W7W"
4606       "=W6W Hl FWLh ChLW MW /o >d 7gKW A\\ 5Z<Z(X8X >Z @v 6W :V  MW       DgI\\$s He 5l Dn EW6W @Y "
4607       ">W4X MWMeM\\!eIe%o HW<] Jq JWLi Dk 2_ @h J~Y(x MX 1iEi HX CX0X ?X 9q   :V   1Z   4\\   GX *m"
4608       " Lz LP*P <X :V  HW   0m        \"l    .~W      :WMeM\\ @UGU          !V%U,V6i/V%U8l JX(X.X(X"
4609       ".X(X.X(X.X(X.Y)X/X2y3s$y Ny Ny Ny NX -X -X -X ,v LX5] Kq Fq Fq Fq Fq GP)P'VKp Gp Ep Ep Ep "
4610       ">X <X -WJj KhKW IhKW IhKW IhKW IhKW IhKW IjEq7m Bq Fq Fq Fq LW ,W ,W ,W &j EW6W Hl Al Al A"
4611       "l Al   Ls AgKW HgKW HgKW HgKW @Z <WLh ;Z               MV &[     =P \"U    V +V )S (S CW4W "
4612       "De DX8X ?\\ 2|                Fh   IX   -VBV Ek 6V4c1kEZ  +V +V    KV BW   0X 4W  Mf At Mx "
4613       "Mq ,X Go :h =X 0l Ej ?X -W       1X 2W 6X(X(s ;o\"s Hy NX  r%X/X'X -X -X2['w LX&X0X4\\ Im NX"
4614       " !m NX0Y(t 9X 2m ;Z 5[5[5X-X FX @~U&W !W -W       @fJW JWJe ?j AeJW En IW 'cIW MW6W MW ,W "
4615       ",W9Y LW ,W7W7W=W6W Fh DWJe AeJW MW .m ;b 6eJW A\\ 5Z<Z)X6X >X ?v 6W :V  MW       CeG[$r Fc "
4616       "2h Am EW6W @Y ?X3W MWMdL\\ cGc#m GW;\\ Hm HWKg Ah /] ?f I~Y(x MX 1iEi HX CX0X ?X 7m   8V   0"
4617       "X   2Z   FX (j Kz   AX :V  HW   -g         Lh    ,~W      :WMdL\\ @UGU          \"V$U-V5i0V$"
4618       "U7i HX(X.X(X.X(X.X(X.X(X.X(X/X2y1o\"y Ny Ny Ny NX -X -X -X ,t JX4\\ Im Bm Bm Bm Bm  %VHm Dm "
4619       "Bm Bm Bm =X <X -WJh HfJW HfJW HfJW HfJW HfJW HfJW HhBn4j ?n Cn Cn Cn KW ,W ,W ,W %h DW6W F"
4620       "h =h =h =h =h   KVMi >eJW GeJW GeJW GeJW ?X ;WJe 9X               MW &Z       =U    W ,W *"
4621       "R &Q BW4W B` AW6W >[ /y                Dd   GX   -VCV Af 5V2a.gBZ  ,W -W    KV CX   0X 4V "
4622       " Kd @t Mx Km *X Ek 6d ;X .h Bh >X .X       1X 1W 7X(X(q 7j Np Ey NX  Mm\"X/X'X -X -X1[(w LX"
4623       "&X0X4\\ Gi LX  Ni LX/X$n 7X 0i 9Z 5[5[6Y-Y GX @~U&W  V -W       >cIW JWIb <g =bIW Ci FW %_G"
4624       "W MW6W MW ,W ,W8Y MW ,W7W7W=W6W Ef CWIb =bIW MW +h 8a 5cIW @Z 4Y:Y*Y5X ?X ?v 6W :V  MW    "
4625       "   AbDY$WMf Ca 0f >k EW6W @Y ?W2W MWK`I[ NaEa i EW;\\ Fi FWIc >e ,\\ =b G~Y(x MX 1iEi HX CX0"
4626       "X ?X 5i   6V   /V   0X   EX &f Iz   AX :V /P;W   *c         Gb    )~W      :WK`I[ @UGU    "
4627       "      #V#U.V4i1V#U6f FX(X.X(X.X(X.X(X.X(X.X(X/X2y/j Ny Ny Ny Ny NX -X -X -X ,p FX4\\ Gi >i "
4628       ">i >i >i  $VEi @i >i >i >i ;X <X -WIf EcIW FcIW FcIW FcIW FcIW FcIW Fd>i0g ;i >i >i >i HW "
4629       ",W ,W ,W #d BW6W Ef ;f ;f ;f ;f   JUJe ;cIW FcIW FcIW FcIW ?X ;WIb 7X               MW %Y "
4630       "      =T    X -X )P %P AW4W ?Z >W6X ?Z ,w                B`   EX   .VBV <] 1V0]*b?[  -W -W"
4631       "    KV CW   /X 4V  I` >t Mx Hg 'X Bf 2` :X +d =b ;X .W       0X 1X 9X&X)m 0d Kj ?y NX  Jg "
4632       "NX/X'X -X -X0[)w LX&X0X3[ Dc IX  Kf LX/Y!g 4X .e 7Z 5Z3Z7Y+Y HX @~U&W  V -W       =`GW JWG"
4633       "^ 7b 9^GW Ad CW \"YDW MW6W MW ,W ,W7Y NW ,W7W7W=W6W B` @WG^ 9^GW MW (c 2] 3_GW @Z 3X:X*Y4Y "
4634       "@X ?v 6W :V  MW       ?_AW$WKb @^ +` 9g CW6W ?W ?X2X NWJ^GY K]B^ Ke CW:[ Dd CWG_ 9` 'Y ;^ "
4635       "F~[)x MX 1iEi HX CX0X ?X 2c   3V   .T   .V   DX $b Gz   AX :V /R>X   &[         ?Z    %~W "
4636       "     :WJ^GY ?UGU          #V +V +V 1b EX&X0X&X0X&X0X&X0X&X0Y'X1X1y,d Ky Ny Ny Ny NX -X -X "
4637       "-X ,j @X3[ Dc 8c 8c 8c 8c  !VBc ;e :e :e :e 9X <X -WFa B`GW E`GW E`GW E`GW E`GW E`GW D`:d*"
4638       "b 7d 9d 9d 9d EW ,W ,W ,W !` @W6W B` 5` 5` 5` 5`   HVHa 7_GW D_GW D_GW D_GW ?X ;WG^ 5X    "
4639       "           MW         7S                   @r                >Y         BS .V,W#Z   ;V -V "
4640       "    7W     ;W  EX     ;\\   6] +Z   5\\ 5Z   <W         7X     %\\       <]    \"X         ([ "
4641       "  4c   E]   /[          (W  W .W       :Y #X 0Z 2X *\\   $W    &W         .Z =WDX 3XDW   I["
4642       "   0Y       8W   -W :V  MW       <Z ;WH[ 9Y &Z 1]  LW ?W   >WGXBU FX=X E` \"W >] @WDY 3Z   "
4643       "2X               C[           >T     :[       KV /TAY                          EWGXBU =UGU"
4644       "   BT       6V +V +V ,Y               ?\\                    +[ 0[ 0[ 0[ 0[   KT=[ 2[ 0[ 0["
4645       " 0[     7Z ;Y .Y .Y .Y .Y .Y -Y2\\\"Z /\\ 1\\ 1\\ 1\\         CZ   3Z /Z /Z /Z /Z   FVCZ 1Y .Y ."
4646       "Y .Y ,W :WDX 2W               LW         7R                                             #S"
4647       "       >W /W     8W     :V                      \"W         5X                  )X         "
4648       "    &Z                  CW  NV .W                   :W    %W           @W  :W             "
4649       " -X   -W :V  MW         LW        FW ?W   >W    NW   0W =W                                "
4650       "      3S       GV /XGZ                          DW  HUGU   AT                            %"
4651       "T                               'R                             JT                         "
4652       "      #T         (X :W  NX               LW                                               "
4653       "        7S       =V /V     7W     :V                      \"W         4X'Q                 "
4654       "&Y             %Z                  DW  NV .W                   :W    %W           @W  :W  "
4655       "            -W   ,W :V  MW         LW        FW ?W   >W    NW   0W =W                     "
4656       "                 3S       GV /j                          CW  HUGU   @T                    "
4657       "        %T                               'P                             HT                "
4658       "               \"Q         'W 9W  NW               KW                                      "
4659       "                 7S       =W 1W     7V     :W                      \"V         2X)R        "
4660       "         &X             #Z                  EW  NW /W                   :W    %W          "
4661       " @W  :W              -W   ,X ;V  NX         LW        FW ?W   >W    NW   0W =W            "
4662       "                          3S       GV /j                          CW  HUGU   @U           "
4663       "                 &U                                                             U         "
4664       "                      \"P         'W 9W  NW               KV                               "
4665       "                        6S       <V 1V     6V     :V                      !V         1Y-U "
4666       "                'X             \"Z                  FW  MV /W                   ;X    %W   "
4667       "        @W  :W              .X   +W ;V  NW         KW        FW ?W   >W    NW   0W =W     "
4668       "                                 3S       GV /h                          AW  HUGU   ?T    "
4669       "                        %T                                                             NT "
4670       "                                        )X 9W  X               KV                         "
4671       "                              6S       <W 3V     6V     9V                      \"V        "
4672       " /Z1X                 (X             !Z                  Ga (V 9a                   ;W    "
4673       "$W           @W  :W              .W   *W ;V  NW         KW        FW ?W   >W    NW   0W =W"
4674       "                                      3S       GV .f                          @W  HUGU   ?"
4675       "U                            &U                                                           "
4676       "  U                                         *W 8W  W               JV                     "
4677       "                                  6S       ;V 3V     6V     :W                      \"V    "
4678       "     .[5[                 *Y              Z                  Ha (W :a                   <X"
4679       "    $W           @W  :W              /X   *X <V  X         KW        FW ?W   >W    NW   0W"
4680       " =W                                      3S       GV +a                          >W  HUGU "
4681       "  >T                            %T                                                        "
4682       "     NT                                         +X 8W !X              (VIV                "
4683       "                                       6S       :V 5V     5U     9W                      \""
4684       "U         +\\;]                 )X              MZ                  Ia (W :a               "
4685       "    =Y    %W           ?W  :W              /W   )[ ?V #[         KW        FW ?W   >W    N"
4686       "W   0W =W                                      3S       GV 'Z                          ;W "
4687       " HUGU   >U                            &U                                                  "
4688       "           U                                         ,W 7W !W              'VIV           "
4689       "                                            6S       :V 6W     6V                         "
4690       "   4V         *_C`                 )Y              LZ                  Ja   :a            "
4691       "      (P7Y    $W           ?W  :W              0X   (b GV +b         JW        FW ?W   >W "
4692       "   NW   0W =W                                      3S       GV                            "
4693       "7W  HUGU   >U                            &U                                               "
4694       "              U                                         -X 7W \"X              'VJW        "
4695       "                                               6S       9V 7V     5U                      "
4696       "      3U         'x                 (Z              KZ                  Ka   :a           "
4697       "       (R:Z    $W           ?W  :W              0X   (b GV +b         JW        FW ?W   >W"
4698       "    NW   0W =W                                      3S       GV                           "
4699       " 7W     #U                            &U                                                  "
4700       "           U                                         -X 7W \"X              &UJW           "
4701       "                                            6S       9W 9W                                "
4702       "            Bu                 ([              IZ                  La   :a                "
4703       "  (T>[    $X           ?W  :W              1X   &a GV +a         IW        FW ?W   >W    N"
4704       "W   0W =W                                      3S       GV                            7W  "
4705       "   $V                            'V                                                       "
4706       "     !V                                         .X 6W #X              %VLW                "
4707       "                                       5S                                                 "
4708       "    2p                 -a                                                       8XE]    %Y"
4709       "           >W  :W              3Z   $_ GV +_         GW        FW ?W   >W    NW   0W =W   "
4710       "                                   3S       GV                            7W     /QGW     "
4711       "                       2QGW                                                            ,QG"
4712       "W                                         0Z 6W %Z              %a                        "
4713       "                               5S                                                     0l  "
4714       "               +a                                                       8p    +_          "
4715       " >W  :W              ;a   !] GV +]         EW        FW ?W   >W    NW   0W =W             "
4716       "                         3S       GV                            7W     /`                 "
4717       "           1`                                                            +`               "
4718       "                          7a 5W -a              #`                                        "
4719       "                                                                     >e                 '`"
4720       "                                                       7o    *^           =W  :W          "
4721       "    ;`    KY GV +Y         AW        FW ?W   >W    NW   0W =W                             "
4722       "         3S       GV                            7W     /`                            1`   "
4723       "                                                         +`                               "
4724       "          7` 4W -`              \"_                                                        "
4725       "                                                     8\\                 #_                "
4726       "                       \"}              3n    )^           =W  :W              ;`     9V   "
4727       "        BW        FW ?W   >W    NW   0W =W                                             'V "
4728       "                           7W     /_                            0_                        "
4729       "                                    *_                                         6` 4W -`   "
4730       "           !]                                                                             "
4731       "                                                  -]                                      "
4732       "  }              3l    ']           <W  :W              ;_     8V           BW        FW ?"
4733       "W   >W    NW   0W =W                                             'V                       "
4734       "     7W     /^                            /^                                              "
4735       "              )^                                         5_ 3W -_               N[        "
4736       "                                                                                          "
4737       "                             ,[                                        M}              2j "
4738       "   &\\           ;W  :W              ;^     7V           BW        FW ?W   >W    NW   0W =W"
4739       "                                                                          7W     -Y       "
4740       "                     *Y                                                            $Y     "
4741       "                                    2^ 2W -^               LX                             "
4742       "                                                                                          "
4743       "        *X                                        J}              /d    #Z           9W  :"
4744       "W              ;\\     5V           BW        FW ?W   >W    NW   0W =W                     "
4745       "                                                     7W                                   "
4746       "                                                                                          "
4747       "            /\\ 0W                 HT                                                      "
4748       "                                                                                          "
4749       "                        I}              *[     NW           6W  :W              ;Z     3V "
4750       "          BW        FW ?W   >W    NW   0W =W                                              "
4751       "                            7W                                                            "
4752       "                                                                             /Z .W        "
4753       "                                                                                          "
4754       "                                                                                       =} "
4755       "                                                                                          "
4756       "                                                                                          "
4757       "                                                                                          "
4758       "                                    D" };
4759 
4760     // Define a 40x38 'danger' color logo (used by cimg::dialog()).
4761     static const unsigned char logo40x38[4576] = {
4762       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,
4763       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,
4764       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,
4765       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,
4766       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,
4767       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,
4768       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,
4769       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,
4770       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,
4771       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,
4772       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,
4773       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,
4774       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,
4775       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,
4776       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,
4777       255,0,1,0,0,0,2,123,123,123,14,200,200,200,1,123,123,0,11,255,255,0,1,0,0,0,10,255,255,0,1,189,189,189,1,0,0,0,2,
4778       123,123,123,13,200,200,200,1,123,123,0,23,255,255,0,1,0,0,0,2,123,123,123,12,200,200,200,1,123,123,0,11,255,255,0,
4779       1,189,189,189,2,0,0,0,1,189,189,189,9,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,11,200,200,200,1,123,123,0,11,
4780       255,255,0,4,0,0,0,10,255,255,0,1,0,0,0,2,123,123,123,10,200,200,200,1,123,123,0,12,255,255,0,4,0,0,0,10,255,255,0,
4781       1,189,189,189,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,12,255,255,0,1,189,189,189,2,0,0,0,1,189,189,189,11,
4782       255,255,0,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,27,255,255,0,1,0,0,0,3,123,123,123,8,200,200,200,1,123,
4783       123,0,26,255,255,0,1,189,189,189,1,0,0,0,3,123,123,123,9,200,200,200,1,123,123,0,24,255,255,0,1,189,189,189,1,0,0,
4784       0,4,123,123,123,10,200,200,200,1,123,123,0,24,0,0,0,5,123,123,123,12,200,200,200,27,123,123,123,14,200,200,200,25,
4785       123,123,123,86,200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 };
4786 
4787     //! Get/set default output stream for the \CImg library messages.
4788     /**
4789        \param file Desired output stream. Set to \c 0 to get the currently used output stream only.
4790        \return Currently used output stream.
4791     **/
4792     inline std::FILE* output(std::FILE *file) {
4793       cimg::mutex(1);
4794       static std::FILE *res = cimg::_stderr();
4795       if (file) res = file;
4796       cimg::mutex(1,0);
4797       return res;
4798     }
4799 
4800     // Return number of available CPU cores.
4801     inline unsigned int nb_cpus() {
4802       unsigned int res = 1;
4803 #if cimg_OS==2
4804       SYSTEM_INFO sysinfo;
4805       GetSystemInfo(&sysinfo);
4806       res = (unsigned int)sysinfo.dwNumberOfProcessors;
4807 #elif cimg_OS == 1
4808       res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
4809 #endif
4810       return res?res:1U;
4811     }
4812 
4813     // Lock/unlock mutex for CImg multi-thread programming.
4814     inline int mutex(const unsigned int n, const int lock_mode) {
4815       switch (lock_mode) {
4816       case 0 : cimg::Mutex_attr().unlock(n); return 0;
4817       case 1 : cimg::Mutex_attr().lock(n); return 0;
4818       default : return cimg::Mutex_attr().trylock(n);
4819       }
4820     }
4821 
4822     //! Display a warning message on the default output stream.
4823     /**
4824        \param format C-string containing the format of the message, as with <tt>std::printf()</tt>.
4825        \note If configuration macro \c cimg_strict_warnings is set, this function throws a
4826        \c CImgWarningException instead.
4827        \warning As the first argument is a format string, it is highly recommended to write
4828        \code
4829        cimg::warn("%s",warning_message);
4830        \endcode
4831        instead of
4832        \code
4833        cimg::warn(warning_message);
4834        \endcode
4835        if \c warning_message can be arbitrary, to prevent nasty memory access.
4836     **/
4837     inline void warn(const char *const format, ...) {
4838       if (cimg::exception_mode()>=1) {
4839         char *const message = new char[16384];
4840         std::va_list ap;
4841         va_start(ap,format);
4842         cimg_vsnprintf(message,16384,format,ap);
4843         va_end(ap);
4844 #ifdef cimg_strict_warnings
4845         throw CImgWarningException(message);
4846 #else
4847         std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message);
4848 #endif
4849         delete[] message;
4850       }
4851     }
4852 
4853     // Execute an external system command.
4854     /**
4855        \param command C-string containing the command line to execute.
4856        \param module_name Module name.
4857        \return Status value of the executed command, whose meaning is OS-dependent.
4858        \note This function is similar to <tt>std::system()</tt>
4859        but it does not open an extra console windows
4860        on Windows-based systems.
4861     **/
4862     inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) {
4863       cimg::unused(module_name);
4864 #ifdef cimg_no_system_calls
4865       return -1;
4866 #else
4867       if (is_verbose) return std::system(command);
4868 #if cimg_OS==1
4869       const unsigned int l = (unsigned int)std::strlen(command);
4870       if (l) {
4871         char *const ncommand = new char[l + 24];
4872         std::memcpy(ncommand,command,l);
4873         std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent.
4874         const int out_val = std::system(ncommand);
4875         delete[] ncommand;
4876         return out_val;
4877       } else return -1;
4878 #elif cimg_OS==2
4879       PROCESS_INFORMATION pi;
4880       STARTUPINFO si;
4881       std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
4882       std::memset(&si,0,sizeof(STARTUPINFO));
4883       GetStartupInfo(&si);
4884       si.cb = sizeof(si);
4885       si.wShowWindow = SW_HIDE;
4886       si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
4887       const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi);
4888       if (res) {
4889         WaitForSingleObject(pi.hProcess,INFINITE);
4890         CloseHandle(pi.hThread);
4891         CloseHandle(pi.hProcess);
4892         return 0;
4893       } else return std::system(command);
4894 #else
4895       return std::system(command);
4896 #endif
4897 #endif
4898     }
4899 
4900     //! Return a reference to a temporary variable of type T.
4901     template<typename T>
4902     inline T& temporary(const T&) {
4903       static T temp;
4904       return temp;
4905     }
4906 
4907     //! Exchange values of variables \c a and \c b.
4908     template<typename T>
4909     inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
4910 
4911     //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2).
4912     template<typename T1, typename T2>
4913     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
4914       cimg::swap(a1,b1); cimg::swap(a2,b2);
4915     }
4916 
4917     //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3).
4918     template<typename T1, typename T2, typename T3>
4919     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
4920       cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
4921     }
4922 
4923     //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4).
4924     template<typename T1, typename T2, typename T3, typename T4>
4925     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
4926       cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
4927     }
4928 
4929     //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5).
4930     template<typename T1, typename T2, typename T3, typename T4, typename T5>
4931     inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
4932       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
4933     }
4934 
4935     //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6).
4936     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
4937     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) {
4938       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
4939     }
4940 
4941     //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7).
4942     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
4943     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,
4944                      T7& a7, T7& b7) {
4945       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
4946     }
4947 
4948     //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8).
4949     template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
4950     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,
4951                      T7& a7, T7& b7, T8& a8, T8& b8) {
4952       cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
4953     }
4954 
4955     //! Return the endianness of the current architecture.
4956     /**
4957        \return \c false for <i>Little Endian</i> or \c true for <i>Big Endian</i>.
4958     **/
4959     inline bool endianness() {
4960       const int x = 1;
4961       return ((unsigned char*)&x)[0]?false:true;
4962     }
4963 
4964     //! Reverse endianness of all elements in a memory buffer.
4965     /**
4966        \param[in,out] buffer Memory buffer whose endianness must be reversed.
4967        \param size Number of buffer elements to reverse.
4968     **/
4969     template<typename T>
4970     inline void invert_endianness(T* const buffer, const cimg_ulong size) {
4971       if (size) switch (sizeof(T)) {
4972         case 1 : break;
4973         case 2 : {
4974           for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) {
4975             const unsigned short val = *(--ptr);
4976             *ptr = (unsigned short)((val>>8) | ((val<<8)));
4977           }
4978         } break;
4979         case 4 : {
4980           for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) {
4981             const unsigned int val = *(--ptr);
4982             *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24);
4983           }
4984         } break;
4985         case 8 : {
4986           const cimg_uint64
4987             m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24,
4988             m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56;
4989           for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) {
4990             const cimg_uint64 val = *(--ptr);
4991             *ptr =  (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) |
4992                      ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56));
4993           }
4994         } break;
4995         default : {
4996           for (T* ptr = buffer + size; ptr>buffer; ) {
4997             unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
4998             for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
4999           }
5000         }
5001         }
5002     }
5003 
5004     //! Reverse endianness of a single variable.
5005     /**
5006        \param[in,out] a Variable to reverse.
5007        \return Reference to reversed variable.
5008     **/
5009     template<typename T>
5010     inline T& invert_endianness(T& a) {
5011       invert_endianness(&a,1);
5012       return a;
5013     }
5014 
5015     // Conversion functions to get more precision when trying to store unsigned ints values as floats.
5016     inline unsigned int float2uint(const float f) {
5017       int tmp = 0;
5018       std::memcpy(&tmp,&f,sizeof(float));
5019       if (tmp>=0) return (unsigned int)f;
5020       unsigned int u;
5021       // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
5022       std::memcpy(&u,&f,sizeof(float));
5023       return ((u)<<1)>>1; // set sign bit to 0.
5024     }
5025 
5026     inline float uint2float(const unsigned int u) {
5027       if (u<(1U<<19)) return (float)u;  // Consider safe storage of unsigned int as floats until 19bits (i.e 524287).
5028       float f;
5029       const unsigned int v = u|(1U<<(8*sizeof(unsigned int)-1)); // set sign bit to 1.
5030       // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
5031       std::memcpy(&f,&v,sizeof(float));
5032       return f;
5033     }
5034 
5035     //! Return the value of a system timer, with a millisecond precision.
5036     /**
5037        \note The timer does not necessarily starts from \c 0.
5038     **/
5039     inline cimg_ulong time() {
5040 #if cimg_OS==1
5041       struct timeval st_time;
5042       gettimeofday(&st_time,0);
5043       return (cimg_ulong)(st_time.tv_usec/1000 + st_time.tv_sec*1000);
5044 #elif cimg_OS==2
5045       SYSTEMTIME st_time;
5046       GetLocalTime(&st_time);
5047       return (cimg_ulong)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour)));
5048 #else
5049       return 0;
5050 #endif
5051     }
5052 
5053     // Implement a tic/toc mechanism to display elapsed time of algorithms.
5054     inline cimg_ulong tictoc(const bool is_tic);
5055 
5056     //! Start tic/toc timer for time measurement between code instructions.
5057     /**
5058        \return Current value of the timer (same value as time()).
5059     **/
5060     inline cimg_ulong tic() {
5061       return cimg::tictoc(true);
5062     }
5063 
5064     //! End tic/toc timer and displays elapsed time from last call to tic().
5065     /**
5066        \return Time elapsed (in ms) since last call to tic().
5067     **/
5068     inline cimg_ulong toc() {
5069       return cimg::tictoc(false);
5070     }
5071 
5072     //! Sleep for a given numbers of milliseconds.
5073     /**
5074        \param milliseconds Number of milliseconds to wait for.
5075        \note This function frees the CPU ressources during the sleeping time.
5076        It can be used to temporize your program properly, without wasting CPU time.
5077     **/
5078     inline void sleep(const unsigned int milliseconds) {
5079 #if cimg_OS==1
5080       struct timespec tv;
5081       tv.tv_sec = milliseconds/1000;
5082       tv.tv_nsec = (milliseconds%1000)*1000000;
5083       nanosleep(&tv,0);
5084 #elif cimg_OS==2
5085       Sleep(milliseconds);
5086 #else
5087       cimg::unused(milliseconds);
5088 #endif
5089     }
5090 
5091     inline unsigned int _wait(const unsigned int milliseconds, cimg_ulong& timer) {
5092       if (!timer) timer = cimg::time();
5093       const cimg_ulong current_time = cimg::time();
5094       if (current_time>=timer + milliseconds) { timer = current_time; return 0; }
5095       const unsigned int time_diff = (unsigned int)(timer + milliseconds - current_time);
5096       timer = current_time + time_diff;
5097       cimg::sleep(time_diff);
5098       return time_diff;
5099     }
5100 
5101     //! Wait for a given number of milliseconds since the last call to wait().
5102     /**
5103        \param milliseconds Number of milliseconds to wait for.
5104        \return Number of milliseconds elapsed since the last call to wait().
5105        \note Same as sleep() with a waiting time computed with regard to the last call
5106        of wait(). It may be used to temporize your program properly, without wasting CPU time.
5107     **/
5108     inline cimg_long wait(const unsigned int milliseconds) {
5109       cimg::mutex(3);
5110       static cimg_ulong timer = 0;
5111       if (!timer) timer = cimg::time();
5112       cimg::mutex(3,0);
5113       return _wait(milliseconds,timer);
5114     }
5115 
5116     // Random number generators.
5117     // CImg may use its own Random Number Generator (RNG) if configuration macro 'cimg_use_rng' is set.
5118     // Use it for instance when you have to deal with concurrent threads trying to call std::srand()
5119     // at the same time!
5120 #ifdef cimg_use_rng
5121 
5122 #include <stdint.h>
5123 
5124     // Use a custom RNG.
5125     inline unsigned int _rand(const unsigned int seed=0, const bool set_seed=false) {
5126       static cimg_ulong next = 0xB16B00B5;
5127       cimg::mutex(4);
5128       if (set_seed) next = (cimg_ulong)seed;
5129       else next = next*1103515245 + 12345U;
5130       cimg::mutex(4,0);
5131       return (unsigned int)(next&0xFFFFFFU);
5132     }
5133 
5134     inline unsigned int srand() {
5135       unsigned int t = (unsigned int)cimg::time();
5136 #if cimg_OS==1
5137       t+=(unsigned int)getpid();
5138 #elif cimg_OS==2
5139       t+=(unsigned int)_getpid();
5140 #endif
5141       return cimg::_rand(t,true);
5142     }
5143 
5144     inline unsigned int srand(const unsigned int seed) {
5145       return _rand(seed,true);
5146     }
5147 
5148     inline double rand(const double val_min, const double val_max) {
5149       const double val = cimg::_rand()/16777215.;
5150       return val_min + (val_max - val_min)*val;
5151     }
5152 
5153 #else
5154 
5155     // Use the system RNG.
5156     inline unsigned int srand() {
5157       const unsigned int t = (unsigned int)cimg::time();
5158 #if cimg_OS==1 || defined(__BORLANDC__)
5159       std::srand(t + (unsigned int)getpid());
5160 #elif cimg_OS==2
5161       std::srand(t + (unsigned int)_getpid());
5162 #else
5163       std::srand(t);
5164 #endif
5165       return t;
5166     }
5167 
5168     inline unsigned int srand(const unsigned int seed) {
5169       std::srand(seed);
5170       return seed;
5171     }
5172 
5173     //! Return a random variable uniformely distributed between [val_min,val_max].
5174     /**
5175     **/
5176     inline double rand(const double val_min, const double val_max) {
5177       const double val = (double)std::rand()/RAND_MAX;
5178       return val_min + (val_max - val_min)*val;
5179     }
5180 #endif
5181 
5182     //! Return a random variable uniformely distributed between [0,val_max].
5183     /**
5184      **/
5185     inline double rand(const double val_max=1) {
5186       return cimg::rand(0,val_max);
5187     }
5188 
5189     //! Return a random variable following a gaussian distribution and a standard deviation of 1.
5190     /**
5191     **/
5192     inline double grand() {
5193       double x1, w;
5194       do {
5195         const double x2 = cimg::rand(-1,1);
5196         x1 = cimg::rand(-1,1);
5197         w = x1*x1 + x2*x2;
5198       } while (w<=0 || w>=1.0);
5199       return x1*std::sqrt((-2*std::log(w))/w);
5200     }
5201 
5202     //! Return a random variable following a Poisson distribution of parameter z.
5203     /**
5204     **/
5205     inline unsigned int prand(const double z) {
5206       if (z<=1.0e-10) return 0;
5207       if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z);
5208       unsigned int k = 0;
5209       const double y = std::exp(-z);
5210       for (double s = 1.0; s>=y; ++k) s*=cimg::rand();
5211       return k - 1;
5212     }
5213 
5214     //! Cut (i.e. clamp) value in specified interval.
5215     template<typename T, typename t>
5216     inline T cut(const T& val, const t& val_min, const t& val_max) {
5217       return val<val_min?(T)val_min:val>val_max?(T)val_max:val;
5218     }
5219 
5220     //! Bitwise-rotate value on the left.
5221     template<typename T>
5222     inline T rol(const T& a, const unsigned int n=1) {
5223       return n?(T)((a<<n)|(a>>((sizeof(T)<<3) - n))):a;
5224     }
5225 
5226     inline float rol(const float a, const unsigned int n=1) {
5227       return (float)rol((int)a,n);
5228     }
5229 
5230     inline double rol(const double a, const unsigned int n=1) {
5231       return (double)rol((cimg_long)a,n);
5232     }
5233 
5234     inline double rol(const long double a, const unsigned int n=1) {
5235       return (double)rol((cimg_long)a,n);
5236     }
5237 
5238 #ifdef cimg_use_half
5239     inline half rol(const half a, const unsigned int n=1) {
5240       return (half)rol((int)a,n);
5241     }
5242 #endif
5243 
5244     //! Bitwise-rotate value on the right.
5245     template<typename T>
5246     inline T ror(const T& a, const unsigned int n=1) {
5247       return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a;
5248     }
5249 
5250     inline float ror(const float a, const unsigned int n=1) {
5251       return (float)ror((int)a,n);
5252     }
5253 
5254     inline double ror(const double a, const unsigned int n=1) {
5255       return (double)ror((cimg_long)a,n);
5256     }
5257 
5258     inline double ror(const long double a, const unsigned int n=1) {
5259       return (double)ror((cimg_long)a,n);
5260     }
5261 
5262 #ifdef cimg_use_half
5263     inline half ror(const half a, const unsigned int n=1) {
5264       return (half)ror((int)a,n);
5265     }
5266 #endif
5267 
5268     //! Return absolute value of a value.
5269     template<typename T>
5270     inline T abs(const T& a) {
5271       return a>=0?a:-a;
5272     }
5273     inline bool abs(const bool a) {
5274       return a;
5275     }
5276     inline int abs(const unsigned char a) {
5277       return (int)a;
5278     }
5279     inline int abs(const unsigned short a) {
5280       return (int)a;
5281     }
5282     inline int abs(const unsigned int a) {
5283       return (int)a;
5284     }
5285     inline int abs(const int a) {
5286       return std::abs(a);
5287     }
5288     inline cimg_int64 abs(const cimg_uint64 a) {
5289       return (cimg_int64)a;
5290     }
5291     inline double abs(const double a) {
5292       return std::fabs(a);
5293     }
5294     inline float abs(const float a) {
5295       return (float)std::fabs((double)a);
5296     }
5297 
5298     //! Return hyperbolic arcosine of a value.
5299     inline double acosh(const double x) {
5300 #if defined(cimg_use_cpp11) && !defined(_MSC_VER)
5301       return std::acosh(x);
5302 #else
5303       return std::log(x + std::sqrt(x*x - 1));
5304 #endif
5305     }
5306 
5307     //! Return hyperbolic arcsine of a value.
5308     inline double asinh(const double x) {
5309 #if defined(cimg_use_cpp11) && !defined(_MSC_VER)
5310       return std::asinh(x);
5311 #else
5312       return std::log(x + std::sqrt(x*x + 1));
5313 #endif
5314     }
5315 
5316     //! Return hyperbolic arctangent of a value.
5317     inline double atanh(const double x) {
5318 #if defined(cimg_use_cpp11) && !defined(_MSC_VER)
5319       return std::atanh(x);
5320 #else
5321       return 0.5*std::log((1.0 + x)/(1.0 - x));
5322 #endif
5323     }
5324 
5325     //! Return the sinc of a given value.
5326     inline double sinc(const double x) {
5327       return x?std::sin(x)/x:1;
5328     }
5329 
5330     //! Return base-2 logarithm of a value.
5331     inline double log2(const double x) {
5332 #if defined(cimg_use_cpp11) && !defined(_MSC_VER)
5333       return std::log2(x);
5334 #else
5335       const double base2 = std::log(2.0);
5336       return std::log(x)/base2;
5337 #endif
5338     }
5339 
5340     //! Return square of a value.
5341     template<typename T>
5342     inline T sqr(const T& val) {
5343       return val*val;
5344     }
5345 
5346     //! Return cubic root of a value.
5347     template<typename T>
5348     inline double cbrt(const T& x) {
5349 #if cimg_use_cpp11==1
5350       return std::cbrt(x);
5351 #else
5352       return x>=0?std::pow((double)x,1.0/3):-std::pow(-(double)x,1.0/3);
5353 #endif
5354     }
5355 
5356     //! Return the minimum between three values.
5357     template<typename t>
5358     inline t min(const t& a, const t& b, const t& c) {
5359       return std::min(std::min(a,b),c);
5360     }
5361 
5362     //! Return the minimum between four values.
5363     template<typename t>
5364     inline t min(const t& a, const t& b, const t& c, const t& d) {
5365       return std::min(std::min(a,b),std::min(c,d));
5366     }
5367 
5368     //! Return the maximum between three values.
5369     template<typename t>
5370     inline t max(const t& a, const t& b, const t& c) {
5371       return std::max(std::max(a,b),c);
5372     }
5373 
5374     //! Return the maximum between four values.
5375     template<typename t>
5376     inline t max(const t& a, const t& b, const t& c, const t& d) {
5377       return std::max(std::max(a,b),std::max(c,d));
5378     }
5379 
5380     //! Return the sign of a value.
5381     template<typename T>
5382     inline T sign(const T& x) {
5383       return (T)(x<0?-1:x>0);
5384     }
5385 
5386     //! Return the nearest power of 2 higher than given value.
5387     template<typename T>
5388     inline cimg_ulong nearest_pow2(const T& x) {
5389       cimg_ulong i = 1;
5390       while (x>i) i<<=1;
5391       return i;
5392     }
5393 
5394     //! Return the modulo of a value.
5395     /**
5396        \param x Input value.
5397        \param m Modulo value.
5398        \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type.
5399     **/
5400     template<typename T>
5401     inline T mod(const T& x, const T& m) {
5402       const double dx = (double)x, dm = (double)m;
5403       return (T)(dx - dm * std::floor(dx / dm));
5404     }
5405     inline int mod(const bool x, const bool m) {
5406       return m?(x?1:0):0;
5407     }
5408     inline int mod(const unsigned char x, const unsigned char m) {
5409       return x%m;
5410     }
5411     inline int mod(const char x, const char m) {
5412 #if defined(CHAR_MAX) && CHAR_MAX==255
5413       return x%m;
5414 #else
5415       return x>=0?x%m:(x%m?m + x%m:0);
5416 #endif
5417     }
5418     inline int mod(const unsigned short x, const unsigned short m) {
5419       return x%m;
5420     }
5421     inline int mod(const short x, const short m) {
5422       return x>=0?x%m:(x%m?m + x%m:0);
5423     }
5424     inline int mod(const unsigned int x, const unsigned int m) {
5425       return (int)(x%m);
5426     }
5427     inline int mod(const int x, const int m) {
5428       return x>=0?x%m:(x%m?m + x%m:0);
5429     }
5430     inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) {
5431       return x%m;
5432     }
5433     inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) {
5434       return x>=0?x%m:(x%m?m + x%m:0);
5435     }
5436 
5437     //! Return the min-mod of two values.
5438     /**
5439        \note <i>minmod(\p a,\p b)</i> is defined to be:
5440        - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
5441        - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
5442     **/
5443     template<typename T>
5444     inline T minmod(const T& a, const T& b) {
5445       return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
5446     }
5447 
5448     template<typename T>
5449     inline T round(const T& x) {
5450       return (T)std::floor((_cimg_Tfloat)x + 0.5f);
5451     }
5452 
5453     //! Return rounded value.
5454     /**
5455        \param x Value to be rounded.
5456        \param y Rounding precision.
5457        \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward).
5458        \return Rounded value, having the same type as input value \c x.
5459     **/
5460     template<typename T>
5461     inline T round(const T& x, const double y, const int rounding_type=0) {
5462       if (y<=0) return x;
5463       if (y==1) switch (rounding_type) {
5464         case 0 : return cimg::round(x);
5465         case 1 : return (T)std::ceil((_cimg_Tfloat)x);
5466         default : return (T)std::floor((_cimg_Tfloat)x);
5467         }
5468       const double sx = (double)x/y, floor = std::floor(sx), delta =  sx - floor;
5469       return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
5470     }
5471 
5472     // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values.
5473     // (contribution by RawTherapee: http://rawtherapee.com/).
5474     template<typename T>
5475     inline T median(T val0, T val1) {
5476       return (val0 + val1)/2;
5477     }
5478 
5479     template<typename T>
5480     inline T median(T val0, T val1, T val2) {
5481       return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1)));
5482     }
5483 
5484     template<typename T>
5485     inline T median(T val0, T val1, T val2, T val3, T val4) {
5486       T tmp = std::min(val0,val1);
5487       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
5488       val3 = std::max(val0,tmp);  val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2);
5489       val1 = tmp; tmp = std::min(val2,val3);
5490       return std::max(val1,tmp);
5491     }
5492 
5493     template<typename T>
5494     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) {
5495       T tmp = std::min(val0,val5);
5496       val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp;
5497       tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4);
5498       val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5);
5499       val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6);
5500       val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp);
5501       tmp = std::min(val1,tmp); val3 = std::max(tmp,val3);
5502       return std::min(val3,val4);
5503     }
5504 
5505     template<typename T>
5506     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) {
5507       T tmp = std::min(val1,val2);
5508       val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
5509       val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
5510       val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1);
5511       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4);
5512       val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7);
5513       val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2);
5514       val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
5515       val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
5516       val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8);
5517       val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6);
5518       val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7);
5519       tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp);
5520       return std::min(val4,val2);
5521     }
5522 
5523     template<typename T>
5524     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8, T val9, T val10, T val11,
5525                     T val12) {
5526       T tmp = std::min(val1,val7);
5527       val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp;
5528       tmp = std::min(val3,val4);  val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8);
5529       val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12);
5530       val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1);
5531       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp;
5532       tmp = std::min(val4,val6);  val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11);
5533       val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp;
5534       tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2);
5535       val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
5536       tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4);
5537       val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
5538       tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12);
5539       tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10);
5540       val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp;
5541       tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9);
5542       val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp;
5543       tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3);
5544       tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10);
5545       val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8);
5546       val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp);
5547       val5 = std::max(tmp,val5); val6 = std::min(val6,val7);
5548       return std::max(val5,val6);
5549     }
5550 
5551     template<typename T>
5552     inline T median(T val0, T val1, T val2, T val3, T val4,
5553                     T val5, T val6, T val7, T val8, T val9,
5554                     T val10, T val11, T val12, T val13, T val14,
5555                     T val15, T val16, T val17, T val18, T val19,
5556                     T val20, T val21, T val22, T val23, T val24) {
5557       T tmp = std::min(val0,val1);
5558       val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
5559       val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3);
5560       val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp;
5561       tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6);
5562       tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10);
5563       val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9);
5564       tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13);
5565       val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12);
5566       tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16);
5567       val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15);
5568       tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19);
5569       val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18);
5570       tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22);
5571       val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21);
5572       tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5);
5573       val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp;
5574       tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3);
5575       tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7);
5576       val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14);
5577       val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14);
5578       val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15);
5579       val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15);
5580       val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16);
5581       val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16);
5582       val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23);
5583       val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23);
5584       val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24);
5585       val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24);
5586       val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22);
5587       val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18);
5588       val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18);
5589       val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp;
5590       tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10);
5591       val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp;
5592       tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11);
5593       tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21);
5594       val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12);
5595       tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22);
5596       val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23);
5597       val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23);
5598       val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24);
5599       val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15);
5600       val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19);
5601       tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp);
5602       val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11);
5603       val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17);
5604       tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12);
5605       val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14);
5606       val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7);
5607       tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14);
5608       tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12);
5609       val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp);
5610       val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp;
5611       val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp;
5612       tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp);
5613       val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp;
5614       tmp = std::min(val10,val20);
5615       return std::max(tmp,val12);
5616     }
5617 
5618     template<typename T>
5619     inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6,
5620                     T val7, T val8, T val9, T val10, T val11, T val12, T val13,
5621                     T val14, T val15, T val16, T val17, T val18, T val19, T val20,
5622                     T val21, T val22, T val23, T val24, T val25, T val26, T val27,
5623                     T val28, T val29, T val30, T val31, T val32, T val33, T val34,
5624                     T val35, T val36, T val37, T val38, T val39, T val40, T val41,
5625                     T val42, T val43, T val44, T val45, T val46, T val47, T val48) {
5626       T tmp = std::min(val0,val32);
5627       val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp;
5628       tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35);
5629       val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp;
5630       tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38);
5631       val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp;
5632       tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41);
5633       val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42);
5634       val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp;
5635       tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45);
5636       val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46);
5637       val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp;
5638       tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16);
5639       val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17);
5640       val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19);
5641       val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp;
5642       tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22);
5643       val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp;
5644       tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25);
5645       val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26);
5646       val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp;
5647       tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29);
5648       val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30);
5649       val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp;
5650       tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32);
5651       val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33);
5652       val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp;
5653       tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36);
5654       val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37);
5655       val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp;
5656       tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40);
5657       val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41);
5658       val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp;
5659       tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44);
5660       val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45);
5661       val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp;
5662       tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8);
5663       val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp;
5664       tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11);
5665       val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp;
5666       tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14);
5667       val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp;
5668       tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25);
5669       val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26);
5670       val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp;
5671       tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29);
5672       val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30);
5673       val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp;
5674       tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41);
5675       val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42);
5676       val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp;
5677       tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45);
5678       val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46);
5679       val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp;
5680       tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33);
5681       val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34);
5682       val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp;
5683       tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37);
5684       val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38);
5685       val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp;
5686       tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16);
5687       val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17);
5688       val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp;
5689       tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20);
5690       val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21);
5691       val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp;
5692       tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32);
5693       val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33);
5694       val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp;
5695       tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36);
5696       val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37);
5697       val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp;
5698       tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48);
5699       val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4);
5700       val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6);
5701       val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
5702       tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13);
5703       val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14);
5704       val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp;
5705       tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21);
5706       val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22);
5707       val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp;
5708       tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29);
5709       val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30);
5710       val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp;
5711       tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37);
5712       val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38);
5713       val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp;
5714       tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45);
5715       val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46);
5716       val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp;
5717       tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33);
5718       val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34);
5719       val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp;
5720       tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41);
5721       val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42);
5722       val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp;
5723       tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16);
5724       val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17);
5725       val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp;
5726       tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24);
5727       val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25);
5728       val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp;
5729       tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32);
5730       val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33);
5731       val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp;
5732       tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40);
5733       val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41);
5734       val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp;
5735       tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48);
5736       val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8);
5737       val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10);
5738       val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp;
5739       tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17);
5740       val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18);
5741       val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp;
5742       tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25);
5743       val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26);
5744       val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp;
5745       tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33);
5746       val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34);
5747       val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp;
5748       tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41);
5749       val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42);
5750       val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp;
5751       tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2);
5752       val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp;
5753       tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7);
5754       val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp;
5755       tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14);
5756       val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15);
5757       val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp;
5758       tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22);
5759       val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23);
5760       val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp;
5761       tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30);
5762       val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31);
5763       val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp;
5764       tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38);
5765       val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39);
5766       val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp;
5767       tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46);
5768       val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47);
5769       val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33);
5770       val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp;
5771       tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40);
5772       val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41);
5773       val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp;
5774       tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48);
5775       val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16);
5776       val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp;
5777       tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21);
5778       val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24);
5779       val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp;
5780       tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29);
5781       val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32);
5782       val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp;
5783       tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37);
5784       val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40);
5785       val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp;
5786       tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45);
5787       val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48);
5788       val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9);
5789       val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
5790       tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16);
5791       val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17);
5792       val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp;
5793       tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24);
5794       val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25);
5795       val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp;
5796       tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32);
5797       val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33);
5798       val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp;
5799       tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40);
5800       val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41);
5801       val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp;
5802       tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48);
5803       val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4);
5804       val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8);
5805       val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp;
5806       tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13);
5807       val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16);
5808       val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp;
5809       tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21);
5810       val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24);
5811       val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp;
5812       tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29);
5813       val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32);
5814       val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp;
5815       tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37);
5816       val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40);
5817       val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp;
5818       tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45);
5819       val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48);
5820       val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5);
5821       val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11);
5822       val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17);
5823       val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23);
5824       val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29);
5825       val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35);
5826       val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41);
5827       val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47);
5828       val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36);
5829       val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42);
5830       val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48);
5831       val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28);
5832       val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34);
5833       val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24);
5834       val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30);
5835       val24 = std::max(val21,val24); val23 = std::min(val23,val26);
5836       return std::max(val23,val24);
5837     }
5838 
5839     //! Return sqrt(x^2 + y^2).
5840     template<typename T>
5841     inline T hypot(const T x, const T y) {
5842       return std::sqrt(x*x + y*y);
5843     }
5844 
5845     template<typename T>
5846     inline T hypot(const T x, const T y, const T z) {
5847       return std::sqrt(x*x + y*y + z*z);
5848     }
5849 
5850     template<typename T>
5851     inline T _hypot(const T x, const T y) { // Slower but more precise version
5852       T nx = cimg::abs(x), ny = cimg::abs(y), t;
5853       if (nx<ny) { t = nx; nx = ny; } else t = ny;
5854       if (nx>0) { t/=nx; return nx*std::sqrt(1 + t*t); }
5855       return 0;
5856     }
5857 
5858     //! Return the factorial of n
5859     inline double factorial(const int n) {
5860       if (n<0) return cimg::type<double>::nan();
5861       if (n<2) return 1;
5862       double res = 2;
5863       for (int i = 3; i<=n; ++i) res*=i;
5864       return res;
5865     }
5866 
5867     //! Return the number of permutations of k objects in a set of n objects.
5868     inline double permutations(const int k, const int n, const bool with_order) {
5869       if (n<0 || k<0) return cimg::type<double>::nan();
5870       if (k>n) return 0;
5871       double res = 1;
5872       for (int i = n; i>=n - k + 1; --i) res*=i;
5873       return with_order?res:res/cimg::factorial(k);
5874     }
5875 
5876     inline double _fibonacci(int exp) {
5877       double
5878         base = (1 + std::sqrt(5.0))/2,
5879         result = 1/std::sqrt(5.0);
5880       while (exp) {
5881         if (exp&1) result*=base;
5882         exp>>=1;
5883         base*=base;
5884       }
5885       return result;
5886     }
5887 
5888     //! Calculate fibonacci number.
5889     // (Precise up to n = 78, less precise for n>78).
5890     inline double fibonacci(const int n) {
5891       if (n<0) return cimg::type<double>::nan();
5892       if (n<3) return 1;
5893       if (n<11) {
5894         cimg_uint64 fn1 = 1, fn2 = 1, fn = 0;
5895         for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
5896         return (double)fn;
5897       }
5898       if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10
5899         return (double)((cimg_uint64)(_fibonacci(n) + 0.5));
5900 
5901       if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93
5902         cimg_uint64
5903           fn1 = (cimg_uint64)1304969544928657ULL,
5904           fn2 = (cimg_uint64)806515533049393ULL,
5905           fn = 0;
5906         for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
5907         return (double)fn;
5908       }
5909       return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation
5910     }
5911 
5912     //! Calculate greatest common divisor.
5913     inline long gcd(long a, long b) {
5914       while (a) { const long c = a; a = b%a; b = c; }
5915       return b;
5916     }
5917 
5918     //! Convert ascii character to lower case.
5919     inline char lowercase(const char x) {
5920       return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a');
5921     }
5922     inline double lowercase(const double x) {
5923       return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a');
5924     }
5925 
5926     //! Convert C-string to lower case.
5927     inline void lowercase(char *const str) {
5928       if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr);
5929     }
5930 
5931     //! Convert ascii character to upper case.
5932     inline char uppercase(const char x) {
5933       return (char)((x<'a'||x>'z')?x:x - 'a' + 'A');
5934     }
5935 
5936     inline double uppercase(const double x) {
5937       return (double)((x<'a'||x>'z')?x:x - 'a' + 'A');
5938     }
5939 
5940     //! Convert C-string to upper case.
5941     inline void uppercase(char *const str) {
5942       if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr);
5943     }
5944 
5945     //! Read value in a C-string.
5946     /**
5947        \param str C-string containing the float value to read.
5948        \return Read value.
5949        \note Same as <tt>std::atof()</tt> extended to manage the retrieval of fractions from C-strings,
5950        as in <em>"1/2"</em>.
5951     **/
5952     inline double atof(const char *const str) {
5953       double x = 0, y = 1;
5954       return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0;
5955     }
5956 
5957     //! Compare the first \p l characters of two C-strings, ignoring the case.
5958     /**
5959        \param str1 C-string.
5960        \param str2 C-string.
5961        \param l Number of characters to compare.
5962        \return \c 0 if the two strings are equal, something else otherwise.
5963        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
5964     **/
5965     inline int strncasecmp(const char *const str1, const char *const str2, const int l) {
5966       if (!l) return 0;
5967       if (!str1) return str2?-1:0;
5968       const char *nstr1 = str1, *nstr2 = str2;
5969       int k, diff = 0; for (k = 0; k<l && !(diff = lowercase(*nstr1) - lowercase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
5970       return k!=l?diff:0;
5971     }
5972 
5973     //! Compare two C-strings, ignoring the case.
5974     /**
5975        \param str1 C-string.
5976        \param str2 C-string.
5977        \return \c 0 if the two strings are equal, something else otherwise.
5978        \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
5979     **/
5980     inline int strcasecmp(const char *const str1, const char *const str2) {
5981       if (!str1) return str2?-1:0;
5982       const int
5983         l1 = (int)std::strlen(str1),
5984         l2 = (int)std::strlen(str2);
5985       return cimg::strncasecmp(str1,str2,1 + (l1<l2?l1:l2));
5986     }
5987 
5988     //! Ellipsize a string.
5989     /**
5990        \param str C-string.
5991        \param l Max number of characters.
5992        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
5993     **/
5994     inline char *strellipsize(char *const str, const unsigned int l=64,
5995                               const bool is_ending=true) {
5996       if (!str) return str;
5997       const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
5998       if (ls<=nl) return str;
5999       if (is_ending) std::strcpy(str + nl - 5,"(...)");
6000       else {
6001         const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
6002         std::strcpy(str + ll,"(...)");
6003         std::memmove(str + ll + 5,str + ls - lr,lr);
6004       }
6005       str[nl] = 0;
6006       return str;
6007     }
6008 
6009     //! Ellipsize a string.
6010     /**
6011        \param str C-string.
6012        \param res output C-string.
6013        \param l Max number of characters.
6014        \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
6015     **/
6016     inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64,
6017                               const bool is_ending=true) {
6018       const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
6019       if (ls<=nl) { std::strcpy(res,str); return res; }
6020       if (is_ending) {
6021         std::strncpy(res,str,nl - 5);
6022         std::strcpy(res + nl -5,"(...)");
6023       } else {
6024         const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
6025         std::strncpy(res,str,ll);
6026         std::strcpy(res + ll,"(...)");
6027         std::strncpy(res + ll + 5,str + ls - lr,lr);
6028       }
6029       res[nl] = 0;
6030       return res;
6031     }
6032 
6033     //! Remove delimiters on the start and/or end of a C-string.
6034     /**
6035        \param[in,out] str C-string to work with (modified at output).
6036        \param delimiter Delimiter character code to remove.
6037        \param is_symmetric Tells if the removal is done only if delimiters are symmetric
6038        (both at the beginning and the end of \c s).
6039        \param is_iterative Tells if the removal is done if several iterations are possible.
6040        \return \c true if delimiters have been removed, \c false otherwise.
6041    **/
6042     inline bool strpare(char *const str, const char delimiter,
6043                         const bool is_symmetric, const bool is_iterative) {
6044       if (!str) return false;
6045       const int l = (int)std::strlen(str);
6046       int p, q;
6047       if (is_symmetric) for (p = 0, q = l - 1; p<q && str[p]==delimiter && str[q]==delimiter; ) {
6048           --q; ++p; if (!is_iterative) break;
6049         } else {
6050         for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
6051         for (q = l - 1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
6052       }
6053       const int n = q - p + 1;
6054       if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
6055       return false;
6056     }
6057 
6058     //! Remove white spaces on the start and/or end of a C-string.
6059     inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) {
6060       if (!str) return false;
6061       const int l = (int)std::strlen(str);
6062       int p, q;
6063       if (is_symmetric) for (p = 0, q = l - 1; p<q && (signed char)str[p]<=' ' && (signed char)str[q]<=' '; ) {
6064           --q; ++p; if (!is_iterative) break;
6065         } else {
6066         for (p = 0; p<l && (signed char)str[p]<=' '; ) { ++p; if (!is_iterative) break; }
6067         for (q = l - 1; q>p && (signed char)str[q]<=' '; ) { --q; if (!is_iterative) break; }
6068       }
6069       const int n = q - p + 1;
6070       if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
6071       return false;
6072     }
6073 
6074     //! Replace reserved characters (for Windows filename) by another character.
6075     /**
6076        \param[in,out] str C-string to work with (modified at output).
6077        \param[in] c Replacement character.
6078     **/
6079     inline void strwindows_reserved(char *const str, const char c='_') {
6080       for (char *s = str; *s; ++s) {
6081         const char i = *s;
6082         if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c;
6083       }
6084     }
6085 
6086     //! Replace escape sequences in C-strings by their binary ascii values.
6087     /**
6088        \param[in,out] str C-string to work with (modified at output).
6089     **/
6090     inline void strunescape(char *const str) {
6091 #define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break;
6092       unsigned int val = 0;
6093       for (char *ns = str, *nd = str; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) {
6094             cimg_strunescape('a','\a');
6095             cimg_strunescape('b','\b');
6096             cimg_strunescape('e',0x1B);
6097             cimg_strunescape('f','\f');
6098             cimg_strunescape('n','\n');
6099             cimg_strunescape('r','\r');
6100             cimg_strunescape('t','\t');
6101             cimg_strunescape('v','\v');
6102             cimg_strunescape('\\','\\');
6103             cimg_strunescape('\'','\'');
6104             cimg_strunescape('\"','\"');
6105             cimg_strunescape('\?','\?');
6106           case 0 : *nd = 0; break;
6107           case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
6108             cimg_sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns;
6109             *nd = (char)val; break;
6110           case 'x' :
6111             cimg_sscanf(++ns,"%x",&val);
6112             while ((*ns>='0' && *ns<='9') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns;
6113             *nd = (char)val; break;
6114           default : *nd = *(ns++);
6115           } else *nd = *(ns++);
6116     }
6117 
6118     // Return a temporary string describing the size of a memory buffer.
6119     inline const char *strbuffersize(const cimg_ulong size);
6120 
6121     // Return string that identifies the running OS.
6122     inline const char *stros() {
6123 #if defined(linux) || defined(__linux) || defined(__linux__)
6124       static const char *const str = "Linux";
6125 #elif defined(sun) || defined(__sun)
6126       static const char *const str = "Sun OS";
6127 #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__)
6128       static const char *const str = "BSD";
6129 #elif defined(sgi) || defined(__sgi)
6130       static const char *const str = "Irix";
6131 #elif defined(__MACOSX__) || defined(__APPLE__)
6132       static const char *const str = "Mac OS";
6133 #elif defined(unix) || defined(__unix) || defined(__unix__)
6134       static const char *const str = "Generic Unix";
6135 #elif defined(_MSC_VER) || defined(WIN32)  || defined(_WIN32) || defined(__WIN32__) || \
6136   defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
6137       static const char *const str = "Windows";
6138 #else
6139       const char
6140         *const _str1 = std::getenv("OSTYPE"),
6141         *const _str2 = _str1?_str1:std::getenv("OS"),
6142         *const str = _str2?_str2:"Unknown OS";
6143 #endif
6144       return str;
6145     }
6146 
6147     //! Return the basename of a filename.
6148     inline const char* basename(const char *const s, const char separator=cimg_file_separator)  {
6149       const char *p = 0, *np = s;
6150       while (np>=s && (p=np)) np = std::strchr(np,separator) + 1;
6151       return p;
6152     }
6153 
6154     // Return a random filename.
6155     inline const char* filenamerand() {
6156       cimg::mutex(6);
6157       static char randomid[9];
6158       cimg::srand();
6159       for (unsigned int k = 0; k<8; ++k) {
6160         const int v = (int)cimg::rand(65535)%3;
6161         randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)):
6162                              (v==1?('a' + ((int)cimg::rand(65535)%26)):
6163                               ('A' + ((int)cimg::rand(65535)%26))));
6164       }
6165       cimg::mutex(6,0);
6166       return randomid;
6167     }
6168 
6169     // Convert filename as a Windows-style filename (short path name).
6170     inline void winformat_string(char *const str) {
6171       if (str && *str) {
6172 #if cimg_OS==2
6173         char *const nstr = new char[MAX_PATH];
6174         if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
6175         delete[] nstr;
6176 #endif
6177       }
6178     }
6179 
6180     // Open a file (with wide character support on Windows).
6181     inline std::FILE *win_fopen(const char *const path, const char *const mode);
6182 
6183     //! Open a file.
6184     /**
6185        \param path Path of the filename to open.
6186        \param mode C-string describing the opening mode.
6187        \return Opened file.
6188        \note Same as <tt>std::fopen()</tt> but throw a \c CImgIOException when
6189        the specified file cannot be opened, instead of returning \c 0.
6190     **/
6191     inline std::FILE *fopen(const char *const path, const char *const mode) {
6192       if (!path)
6193         throw CImgArgumentException("cimg::fopen(): Specified file path is (null).");
6194       if (!mode)
6195         throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).",
6196                                     path);
6197       std::FILE *res = 0;
6198       if (*path=='-' && (!path[1] || path[1]=='.')) {
6199         res = (*mode=='r')?cimg::_stdin():cimg::_stdout();
6200 #if cimg_OS==2
6201         if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode.
6202 #ifdef __BORLANDC__
6203           if (setmode(_fileno(res),0x8000)==-1) res = 0;
6204 #else
6205           if (_setmode(_fileno(res),0x8000)==-1) res = 0;
6206 #endif
6207         }
6208 #endif
6209       } else res = std_fopen(path,mode);
6210       if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.",
6211                                       path,mode);
6212       return res;
6213     }
6214 
6215     //! Close a file.
6216     /**
6217        \param file File to close.
6218        \return \c 0 if file has been closed properly, something else otherwise.
6219        \note Same as <tt>std::fclose()</tt> but display a warning message if
6220        the file has not been closed properly.
6221     **/
6222     inline int fclose(std::FILE *file) {
6223       if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; }
6224       if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0;
6225       const int errn = std::fclose(file);
6226       if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.",
6227                         errn);
6228       return errn;
6229     }
6230 
6231     //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows).
6232     inline int fseek(FILE *stream, cimg_long offset, int origin) {
6233 #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
6234       return _fseeki64(stream,(__int64)offset,origin);
6235 #else
6236       return std::fseek(stream,offset,origin);
6237 #endif
6238     }
6239 
6240     //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows).
6241     inline cimg_long ftell(FILE *stream) {
6242 #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
6243       return (cimg_long)_ftelli64(stream);
6244 #else
6245       return (cimg_long)std::ftell(stream);
6246 #endif
6247     }
6248 
6249     //! Check if a path is a directory.
6250     /**
6251        \param path Specified path to test.
6252     **/
6253     inline bool is_directory(const char *const path) {
6254       if (!path || !*path) return false;
6255 #if cimg_OS==1
6256       struct stat st_buf;
6257       return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode));
6258 #elif cimg_OS==2
6259       const unsigned int res = (unsigned int)GetFileAttributesA(path);
6260       return res==INVALID_FILE_ATTRIBUTES?false:(res&16);
6261 #else
6262       return false;
6263 #endif
6264     }
6265 
6266     //! Check if a path is a file.
6267     /**
6268        \param path Specified path to test.
6269     **/
6270     inline bool is_file(const char *const path) {
6271       if (!path || !*path) return false;
6272       std::FILE *const file = std_fopen(path,"rb");
6273       if (!file) return false;
6274       std::fclose(file);
6275       return !is_directory(path);
6276     }
6277 
6278     //! Get file size.
6279     /**
6280        \param filename Specified filename to get size from.
6281        \return File size or '-1' if file does not exist.
6282     **/
6283     inline cimg_int64 fsize(const char *const filename) {
6284       std::FILE *const file = std::fopen(filename,"rb");
6285       if (!file) return (cimg_int64)-1;
6286       std::fseek(file,0,SEEK_END);
6287       const cimg_int64 siz = (cimg_int64)std::ftell(file);
6288       std::fclose(file);
6289       return siz;
6290     }
6291 
6292     //! Get last write time of a given file or directory (multiple-attributes version).
6293     /**
6294        \param path Specified path to get attributes from.
6295        \param[in,out] attr Type of requested time attributes.
6296                       Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
6297                       Replaced by read attributes after return (or -1 if an error occured).
6298        \param nb_attr Number of attributes to read/write.
6299        \return Latest read attribute.
6300     **/
6301     template<typename T>
6302     inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) {
6303 #define _cimg_fdate_err() for (unsigned int i = 0; i<nb_attr; ++i) attr[i] = (T)-1
6304       int res = -1;
6305       if (!path || !*path) { _cimg_fdate_err(); return -1; }
6306       cimg::mutex(6);
6307 #if cimg_OS==2
6308       HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
6309       if (file!=INVALID_HANDLE_VALUE) {
6310         FILETIME _ft;
6311         SYSTEMTIME ft;
6312         if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) {
6313           for (unsigned int i = 0; i<nb_attr; ++i) {
6314             res = (int)(attr[i]==0?ft.wYear:attr[i]==1?ft.wMonth:attr[i]==2?ft.wDay:
6315                         attr[i]==3?ft.wDayOfWeek:attr[i]==4?ft.wHour:attr[i]==5?ft.wMinute:
6316                         attr[i]==6?ft.wSecond:-1);
6317             attr[i] = (T)res;
6318           }
6319         } else _cimg_fdate_err();
6320         CloseHandle(file);
6321       } else _cimg_fdate_err();
6322 #elif cimg_OS==1
6323       struct stat st_buf;
6324       if (!stat(path,&st_buf)) {
6325         const time_t _ft = st_buf.st_mtime;
6326         const struct tm& ft = *std::localtime(&_ft);
6327         for (unsigned int i = 0; i<nb_attr; ++i) {
6328           res = (int)(attr[i]==0?ft.tm_year + 1900:attr[i]==1?ft.tm_mon + 1:attr[i]==2?ft.tm_mday:
6329                       attr[i]==3?ft.tm_wday:attr[i]==4?ft.tm_hour:attr[i]==5?ft.tm_min:
6330                       attr[i]==6?ft.tm_sec:-1);
6331           attr[i] = (T)res;
6332         }
6333       } else _cimg_fdate_err();
6334 #endif
6335       cimg::mutex(6,0);
6336       return res;
6337     }
6338 
6339     //! Get last write time of a given file or directory (single-attribute version).
6340     /**
6341        \param path Specified path to get attributes from.
6342        \param attr Type of requested time attributes.
6343                    Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
6344        \return Specified attribute or -1 if an error occured.
6345     **/
6346     inline int fdate(const char *const path, unsigned int attr) {
6347       int out = (int)attr;
6348       return fdate(path,&out,1);
6349     }
6350 
6351     //! Get current local time (multiple-attributes version).
6352     /**
6353        \param[in,out] attr Type of requested time attributes.
6354                            Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
6355                            Replaced by read attributes after return (or -1 if an error occured).
6356        \param nb_attr Number of attributes to read/write.
6357        \return Latest read attribute.
6358     **/
6359     template<typename T>
6360     inline int date(T *attr, const unsigned int nb_attr) {
6361       int res = -1;
6362       cimg::mutex(6);
6363 #if cimg_OS==2
6364       SYSTEMTIME st;
6365       GetLocalTime(&st);
6366       for (unsigned int i = 0; i<nb_attr; ++i) {
6367         res = (int)(attr[i]==0?st.wYear:attr[i]==1?st.wMonth:attr[i]==2?st.wDay:
6368                     attr[i]==3?st.wDayOfWeek:attr[i]==4?st.wHour:attr[i]==5?st.wMinute:
6369                     attr[i]==6?st.wSecond:-1);
6370         attr[i] = (T)res;
6371       }
6372 #else
6373       time_t _st;
6374       std::time(&_st);
6375       struct tm *st = std::localtime(&_st);
6376       for (unsigned int i = 0; i<nb_attr; ++i) {
6377         res = (int)(attr[i]==0?st->tm_year + 1900:attr[i]==1?st->tm_mon + 1:attr[i]==2?st->tm_mday:
6378                     attr[i]==3?st->tm_wday:attr[i]==4?st->tm_hour:attr[i]==5?st->tm_min:
6379                     attr[i]==6?st->tm_sec:-1);
6380         attr[i] = (T)res;
6381       }
6382 #endif
6383       cimg::mutex(6,0);
6384       return res;
6385     }
6386 
6387     //! Get current local time (single-attribute version).
6388     /**
6389        \param attr Type of requested time attribute.
6390                    Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
6391        \return Specified attribute or -1 if an error occured.
6392     **/
6393     inline int date(unsigned int attr) {
6394       int out = (int)attr;
6395       return date(&out,1);
6396     }
6397 
6398     // Get/set path to store temporary files.
6399     inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false);
6400 
6401     // Get/set path to the <i>Program Files/</i> directory (Windows only).
6402 #if cimg_OS==2
6403     inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false);
6404 #endif
6405 
6406     // Get/set path to the ImageMagick's \c convert binary.
6407     inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false);
6408 
6409     // Get/set path to the GraphicsMagick's \c gm binary.
6410     inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false);
6411 
6412     // Get/set path to the XMedcon's \c medcon binary.
6413     inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false);
6414 
6415     // Get/set path to the FFMPEG's \c ffmpeg binary.
6416     inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false);
6417 
6418     // Get/set path to the \c gzip binary.
6419     inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false);
6420 
6421     // Get/set path to the \c gunzip binary.
6422     inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false);
6423 
6424     // Get/set path to the \c dcraw binary.
6425     inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false);
6426 
6427     // Get/set path to the \c wget binary.
6428     inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false);
6429 
6430     // Get/set path to the \c curl binary.
6431     inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false);
6432 
6433     //! Split filename into two C-strings \c body and \c extension.
6434     /**
6435        filename and body must not overlap!
6436     **/
6437     inline const char *split_filename(const char *const filename, char *const body=0) {
6438       if (!filename) { if (body) *body = 0; return 0; }
6439       const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.') + 1) {}
6440       if (p==filename) {
6441         if (body) std::strcpy(body,filename);
6442         return filename + std::strlen(filename);
6443       }
6444       const unsigned int l = (unsigned int)(p - filename - 1);
6445       if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; }
6446       return p;
6447     }
6448 
6449     //! Generate a numbered version of a filename.
6450     inline char* number_filename(const char *const filename, const int number,
6451                                  const unsigned int digits, char *const str) {
6452       if (!filename) { if (str) *str = 0; return 0; }
6453       char *const format = new char[1024], *const body = new char[1024];
6454       const char *const ext = cimg::split_filename(filename,body);
6455       if (*ext) cimg_snprintf(format,1024,"%%s_%%.%ud.%%s",digits);
6456       else cimg_snprintf(format,1024,"%%s_%%.%ud",digits);
6457       cimg_sprintf(str,format,body,number,ext);
6458       delete[] format; delete[] body;
6459       return str;
6460     }
6461 
6462     //! Read data from file.
6463     /**
6464        \param[out] ptr Pointer to memory buffer that will contain the binary data read from file.
6465        \param nmemb Number of elements to read.
6466        \param stream File to read data from.
6467        \return Number of read elements.
6468        \note Same as <tt>std::fread()</tt> but may display warning message if all elements could not be read.
6469     **/
6470     template<typename T>
6471     inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) {
6472       if (!ptr || !stream)
6473         throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.",
6474                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
6475       if (!nmemb) return 0;
6476       const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
6477       size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
6478       do {
6479         l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
6480         l_al_read = std::fread((void*)(ptr + al_read),sizeof(T),l_to_read,stream);
6481         al_read+=l_al_read;
6482         to_read-=l_al_read;
6483       } while (l_to_read==l_al_read && to_read>0);
6484       if (to_read>0)
6485         warn("cimg::fread(): Only %lu/%lu elements could be read from file.",
6486              (unsigned long)al_read,(unsigned long)nmemb);
6487       return al_read;
6488     }
6489 
6490     //! Write data to file.
6491     /**
6492        \param ptr Pointer to memory buffer containing the binary data to write on file.
6493        \param nmemb Number of elements to write.
6494        \param[out] stream File to write data on.
6495        \return Number of written elements.
6496        \note Similar to <tt>std::fwrite</tt> but may display warning messages if all elements could not be written.
6497     **/
6498     template<typename T>
6499     inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) {
6500       if (!ptr || !stream)
6501         throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.",
6502                                     nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
6503       if (!nmemb) return 0;
6504       const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
6505       size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
6506       do {
6507         l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
6508         l_al_write = std::fwrite((void*)(ptr + al_write),sizeof(T),l_to_write,stream);
6509         al_write+=l_al_write;
6510         to_write-=l_al_write;
6511       } while (l_to_write==l_al_write && to_write>0);
6512       if (to_write>0)
6513         warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.",
6514              (unsigned long)al_write,(unsigned long)nmemb);
6515       return al_write;
6516     }
6517 
6518     //! Create an empty file.
6519     /**
6520        \param file Input file (can be \c 0 if \c filename is set).
6521        \param filename Filename, as a C-string (can be \c 0 if \c file is set).
6522     **/
6523     inline void fempty(std::FILE *const file, const char *const filename) {
6524       if (!file && !filename)
6525         throw CImgArgumentException("cimg::fempty(): Specified filename is (null).");
6526       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
6527       if (!file) cimg::fclose(nfile);
6528     }
6529 
6530     // Try to guess format from an image file.
6531     inline const char *ftype(std::FILE *const file, const char *const filename);
6532 
6533     // Load file from network as a local temporary file.
6534     inline char *load_network(const char *const url, char *const filename_local,
6535                               const unsigned int timeout=0, const bool try_fallback=false,
6536                               const char *const referer=0);
6537 
6538     //! Return options specified on the command line.
6539     inline const char* option(const char *const name, const int argc, const char *const *const argv,
6540                               const char *const defaut, const char *const usage, const bool reset_static) {
6541       static bool first = true, visu = false;
6542       if (reset_static) { first = true; return 0; }
6543       const char *res = 0;
6544       if (first) {
6545         first = false;
6546         visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
6547         visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
6548         visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
6549       }
6550       if (!name && visu) {
6551         if (usage) {
6552           std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
6553           std::fprintf(cimg::output(),": %s",usage);
6554           std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time);
6555         }
6556         if (defaut) std::fprintf(cimg::output(),"%s\n",defaut);
6557       }
6558       if (name) {
6559         if (argc>0) {
6560           int k = 0;
6561           while (k<argc && std::strcmp(argv[k],name)) ++k;
6562           res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
6563         } else res = defaut;
6564         if (visu && usage) std::fprintf(cimg::output(),"    %s%-16s%s %-24s %s%s%s\n",
6565                                         cimg::t_bold,name,cimg::t_normal,res?res:"0",
6566                                         cimg::t_green,usage,cimg::t_normal);
6567       }
6568       return res;
6569     }
6570 
6571     inline const char* option(const char *const name, const int argc, const char *const *const argv,
6572                               const char *const defaut, const char *const usage=0) {
6573       return option(name,argc,argv,defaut,usage,false);
6574     }
6575 
6576     inline bool option(const char *const name, const int argc, const char *const *const argv,
6577                        const bool defaut, const char *const usage=0) {
6578       const char *const s = cimg::option(name,argc,argv,(char*)0);
6579       const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut;
6580       cimg::option(name,0,0,res?"true":"false",usage);
6581       return res;
6582     }
6583 
6584     inline int option(const char *const name, const int argc, const char *const *const argv,
6585                       const int defaut, const char *const usage=0) {
6586       const char *const s = cimg::option(name,argc,argv,(char*)0);
6587       const int res = s?std::atoi(s):defaut;
6588       char *const tmp = new char[256];
6589       cimg_snprintf(tmp,256,"%d",res);
6590       cimg::option(name,0,0,tmp,usage);
6591       delete[] tmp;
6592       return res;
6593     }
6594 
6595     inline char option(const char *const name, const int argc, const char *const *const argv,
6596                        const char defaut, const char *const usage=0) {
6597       const char *const s = cimg::option(name,argc,argv,(char*)0);
6598       const char res = s?*s:defaut;
6599       char tmp[8];
6600       *tmp = res; tmp[1] = 0;
6601       cimg::option(name,0,0,tmp,usage);
6602       return res;
6603     }
6604 
6605     inline float option(const char *const name, const int argc, const char *const *const argv,
6606                         const float defaut, const char *const usage=0) {
6607       const char *const s = cimg::option(name,argc,argv,(char*)0);
6608       const float res = s?(float)cimg::atof(s):defaut;
6609       char *const tmp = new char[256];
6610       cimg_snprintf(tmp,256,"%g",res);
6611       cimg::option(name,0,0,tmp,usage);
6612       delete[] tmp;
6613       return res;
6614     }
6615 
6616     inline double option(const char *const name, const int argc, const char *const *const argv,
6617                          const double defaut, const char *const usage=0) {
6618       const char *const s = cimg::option(name,argc,argv,(char*)0);
6619       const double res = s?cimg::atof(s):defaut;
6620       char *const tmp = new char[256];
6621       cimg_snprintf(tmp,256,"%g",res);
6622       cimg::option(name,0,0,tmp,usage);
6623       delete[] tmp;
6624       return res;
6625     }
6626 
6627     //! Print information about \CImg environement variables.
6628     /**
6629        \note Output is done on the default output stream.
6630     **/
6631     inline void info() {
6632       std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n",
6633                    cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
6634                    cimg::t_normal,cimg_date,cimg_time);
6635 
6636       std::fprintf(cimg::output(),"  > Operating System:       %s%-13s%s %s('cimg_OS'=%d)%s\n",
6637                    cimg::t_bold,
6638                    cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"),
6639                    cimg::t_normal,cimg::t_green,
6640                    cimg_OS,
6641                    cimg::t_normal);
6642 
6643       std::fprintf(cimg::output(),"  > CPU endianness:         %s%s Endian%s\n",
6644                    cimg::t_bold,
6645                    cimg::endianness()?"Big":"Little",
6646                    cimg::t_normal);
6647 
6648       std::fprintf(cimg::output(),"  > Verbosity mode:         %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
6649                    cimg::t_bold,
6650                    cimg_verbosity==0?"Quiet":
6651                    cimg_verbosity==1?"Console":
6652                    cimg_verbosity==2?"Dialog":
6653                    cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings",
6654                    cimg::t_normal,cimg::t_green,
6655                    cimg_verbosity,
6656                    cimg::t_normal);
6657 
6658       std::fprintf(cimg::output(),"  > Stricts warnings:       %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
6659                    cimg::t_bold,
6660 #ifdef cimg_strict_warnings
6661                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6662 #else
6663                    "No",cimg::t_normal,cimg::t_green,"undefined",
6664 #endif
6665                    cimg::t_normal);
6666 
6667       std::fprintf(cimg::output(),"  > Support for C++11:      %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n",
6668                    cimg::t_bold,
6669                    cimg_use_cpp11?"Yes":"No",
6670                    cimg::t_normal,cimg::t_green,
6671                    (int)cimg_use_cpp11,
6672                    cimg::t_normal);
6673 
6674       std::fprintf(cimg::output(),"  > Using VT100 messages:   %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
6675                    cimg::t_bold,
6676 #ifdef cimg_use_vt100
6677                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6678 #else
6679                    "No",cimg::t_normal,cimg::t_green,"undefined",
6680 #endif
6681                    cimg::t_normal);
6682 
6683       std::fprintf(cimg::output(),"  > Display type:           %s%-13s%s %s('cimg_display'=%d)%s\n",
6684                    cimg::t_bold,
6685                    cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
6686                    cimg::t_normal,cimg::t_green,
6687                    (int)cimg_display,
6688                    cimg::t_normal);
6689 
6690 #if cimg_display==1
6691       std::fprintf(cimg::output(),"  > Using XShm for X11:     %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
6692                    cimg::t_bold,
6693 #ifdef cimg_use_xshm
6694                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6695 #else
6696                    "No",cimg::t_normal,cimg::t_green,"undefined",
6697 #endif
6698                    cimg::t_normal);
6699 
6700       std::fprintf(cimg::output(),"  > Using XRand for X11:    %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
6701                    cimg::t_bold,
6702 #ifdef cimg_use_xrandr
6703                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6704 #else
6705                    "No",cimg::t_normal,cimg::t_green,"undefined",
6706 #endif
6707                    cimg::t_normal);
6708 #endif
6709       std::fprintf(cimg::output(),"  > Using OpenMP:           %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
6710                    cimg::t_bold,
6711 #ifdef cimg_use_openmp
6712                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6713 #else
6714                    "No",cimg::t_normal,cimg::t_green,"undefined",
6715 #endif
6716                    cimg::t_normal);
6717       std::fprintf(cimg::output(),"  > Using PNG library:      %s%-13s%s %s('cimg_use_png' %s)%s\n",
6718                    cimg::t_bold,
6719 #ifdef cimg_use_png
6720                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6721 #else
6722                    "No",cimg::t_normal,cimg::t_green,"undefined",
6723 #endif
6724                    cimg::t_normal);
6725       std::fprintf(cimg::output(),"  > Using JPEG library:     %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
6726                    cimg::t_bold,
6727 #ifdef cimg_use_jpeg
6728                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6729 #else
6730                    "No",cimg::t_normal,cimg::t_green,"undefined",
6731 #endif
6732                    cimg::t_normal);
6733 
6734       std::fprintf(cimg::output(),"  > Using TIFF library:     %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
6735                    cimg::t_bold,
6736 #ifdef cimg_use_tiff
6737                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6738 #else
6739                    "No",cimg::t_normal,cimg::t_green,"undefined",
6740 #endif
6741                    cimg::t_normal);
6742 
6743       std::fprintf(cimg::output(),"  > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n",
6744                    cimg::t_bold,
6745 #ifdef cimg_use_magick
6746                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6747 #else
6748                    "No",cimg::t_normal,cimg::t_green,"undefined",
6749 #endif
6750                    cimg::t_normal);
6751 
6752       std::fprintf(cimg::output(),"  > Using FFTW3 library:    %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
6753                    cimg::t_bold,
6754 #ifdef cimg_use_fftw3
6755                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6756 #else
6757                    "No",cimg::t_normal,cimg::t_green,"undefined",
6758 #endif
6759                    cimg::t_normal);
6760 
6761       std::fprintf(cimg::output(),"  > Using LAPACK library:   %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
6762                    cimg::t_bold,
6763 #ifdef cimg_use_lapack
6764                    "Yes",cimg::t_normal,cimg::t_green,"defined",
6765 #else
6766                    "No",cimg::t_normal,cimg::t_green,"undefined",
6767 #endif
6768                    cimg::t_normal);
6769 
6770       char *const tmp = new char[1024];
6771       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path());
6772       std::fprintf(cimg::output(),"  > Path of ImageMagick:    %s%-13s%s\n",
6773                    cimg::t_bold,
6774                    tmp,
6775                    cimg::t_normal);
6776 
6777       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path());
6778       std::fprintf(cimg::output(),"  > Path of GraphicsMagick: %s%-13s%s\n",
6779                    cimg::t_bold,
6780                    tmp,
6781                    cimg::t_normal);
6782 
6783       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path());
6784       std::fprintf(cimg::output(),"  > Path of 'medcon':       %s%-13s%s\n",
6785                    cimg::t_bold,
6786                    tmp,
6787                    cimg::t_normal);
6788 
6789       cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path());
6790       std::fprintf(cimg::output(),"  > Temporary path:         %s%-13s%s\n",
6791                    cimg::t_bold,
6792                    tmp,
6793                    cimg::t_normal);
6794 
6795       std::fprintf(cimg::output(),"\n");
6796       delete[] tmp;
6797     }
6798 
6799     // Declare LAPACK function signatures if LAPACK support is enabled.
6800 #ifdef cimg_use_lapack
6801     template<typename T>
6802     inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
6803       dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
6804     }
6805 
6806     inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
6807       sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
6808     }
6809 
6810     template<typename T>
6811     inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
6812       dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
6813     }
6814 
6815     inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
6816       sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
6817     }
6818 
6819     template<typename T>
6820     inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
6821                       T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
6822       dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
6823     }
6824 
6825     inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
6826                       float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
6827       sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
6828     }
6829 
6830     template<typename T>
6831     inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
6832       int one = 1;
6833       dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
6834     }
6835 
6836     inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
6837       int one = 1;
6838       sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
6839     }
6840 
6841     template<typename T>
6842     inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
6843       dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
6844     }
6845 
6846     inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
6847       ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
6848     }
6849 
6850     template<typename T>
6851     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
6852 		      T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO){
6853       dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
6854     }
6855 
6856     inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
6857 		      float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO){
6858       sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
6859     }
6860 
6861 #endif
6862 
6863     // End of the 'cimg' namespace
6864   }
6865 
6866   /*------------------------------------------------
6867    #
6868    #
6869    #   Definition of mathematical operators and
6870    #   external functions.
6871    #
6872    #
6873    -------------------------------------------------*/
6874 
6875 #define _cimg_create_ext_operators(typ) \
6876   template<typename T> \
6877   inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
6878     return img + val; \
6879   } \
6880   template<typename T> \
6881   inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
6882     typedef typename cimg::superset<T,typ>::type Tt; \
6883     return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
6884   } \
6885   template<typename T> \
6886   inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
6887     return img*val; \
6888   } \
6889   template<typename T> \
6890   inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
6891     return val*img.get_invert(); \
6892   } \
6893   template<typename T> \
6894   inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
6895     return img & val; \
6896   } \
6897   template<typename T> \
6898   inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
6899     return img | val; \
6900   } \
6901   template<typename T> \
6902   inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
6903     return img ^ val; \
6904   } \
6905   template<typename T> \
6906   inline bool operator==(const typ val, const CImg<T>& img) {   \
6907     return img == val; \
6908   } \
6909   template<typename T> \
6910   inline bool operator!=(const typ val, const CImg<T>& img) { \
6911     return img != val; \
6912   }
6913 
6914   _cimg_create_ext_operators(bool)
6915   _cimg_create_ext_operators(unsigned char)
6916   _cimg_create_ext_operators(char)
6917   _cimg_create_ext_operators(signed char)
6918   _cimg_create_ext_operators(unsigned short)
6919   _cimg_create_ext_operators(short)
6920   _cimg_create_ext_operators(unsigned int)
6921   _cimg_create_ext_operators(int)
6922   _cimg_create_ext_operators(cimg_uint64)
6923   _cimg_create_ext_operators(cimg_int64)
6924   _cimg_create_ext_operators(float)
6925   _cimg_create_ext_operators(double)
6926   _cimg_create_ext_operators(long double)
6927 
6928   template<typename T>
6929   inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
6930     return img + expression;
6931   }
6932 
6933   template<typename T>
6934   inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
6935     return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img;
6936   }
6937 
6938   template<typename T>
6939   inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
6940     return img*expression;
6941   }
6942 
6943   template<typename T>
6944   inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
6945     return expression*img.get_invert();
6946   }
6947 
6948   template<typename T>
6949   inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
6950     return img & expression;
6951   }
6952 
6953   template<typename T>
6954   inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
6955     return img | expression;
6956   }
6957 
6958   template<typename T>
6959   inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
6960     return img ^ expression;
6961   }
6962 
6963   template<typename T>
6964   inline bool operator==(const char *const expression, const CImg<T>& img) {
6965     return img==expression;
6966   }
6967 
6968   template<typename T>
6969   inline bool operator!=(const char *const expression, const CImg<T>& img) {
6970     return img!=expression;
6971   }
6972 
6973   template<typename T>
6974   inline CImg<T> transpose(const CImg<T>& instance) {
6975     return instance.get_transpose();
6976   }
6977 
6978   template<typename T>
6979   inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) {
6980     return instance.get_invert();
6981   }
6982 
6983   template<typename T>
6984   inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance) {
6985     return instance.get_pseudoinvert();
6986   }
6987 
6988 #define _cimg_create_ext_pointwise_function(name) \
6989   template<typename T> \
6990   inline CImg<_cimg_Tfloat> name(const CImg<T>& instance) { \
6991     return instance.get_##name(); \
6992   }
6993 
6994   _cimg_create_ext_pointwise_function(sqr)
6995   _cimg_create_ext_pointwise_function(sqrt)
6996   _cimg_create_ext_pointwise_function(exp)
6997   _cimg_create_ext_pointwise_function(log)
6998   _cimg_create_ext_pointwise_function(log2)
6999   _cimg_create_ext_pointwise_function(log10)
7000   _cimg_create_ext_pointwise_function(abs)
7001   _cimg_create_ext_pointwise_function(sign)
7002   _cimg_create_ext_pointwise_function(cos)
7003   _cimg_create_ext_pointwise_function(sin)
7004   _cimg_create_ext_pointwise_function(sinc)
7005   _cimg_create_ext_pointwise_function(tan)
7006   _cimg_create_ext_pointwise_function(acos)
7007   _cimg_create_ext_pointwise_function(asin)
7008   _cimg_create_ext_pointwise_function(atan)
7009   _cimg_create_ext_pointwise_function(cosh)
7010   _cimg_create_ext_pointwise_function(sinh)
7011   _cimg_create_ext_pointwise_function(tanh)
7012   _cimg_create_ext_pointwise_function(acosh)
7013   _cimg_create_ext_pointwise_function(asinh)
7014   _cimg_create_ext_pointwise_function(atanh)
7015 
7016   /*-----------------------------------
7017    #
7018    # Define the CImgDisplay structure
7019    #
7020    ----------------------------------*/
7021   //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events).
7022   /**
7023      CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window
7024      (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems).
7025      If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter
7026      a minimal mode where warning messages will be outputed each time the program is trying to call one of the
7027      CImgDisplay method.
7028 
7029      The configuration variable \c cimg_display tells about the graphic library used.
7030      It is set automatically by \CImg when one of these graphic libraries has been detected.
7031      But, you can override its value if necessary. Valid choices are:
7032      - 0: Disable display capabilities.
7033      - 1: Use \b X-Window (X11) library.
7034      - 2: Use \b GDI32 library.
7035 
7036      Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay.
7037   **/
7038   struct CImgDisplay {
7039     cimg_ulong _timer, _fps_frames, _fps_timer;
7040     unsigned int _width, _height, _normalization;
7041     float _fps_fps, _min, _max;
7042     bool _is_fullscreen;
7043     char *_title;
7044     unsigned int _window_width, _window_height, _button, *_keys, *_released_keys;
7045     int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
7046     bool _is_closed, _is_resized, _is_moved, _is_event,
7047       _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
7048       _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
7049       _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
7050       _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
7051       _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
7052       _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
7053       _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
7054       _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
7055       _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT,
7056       _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3,
7057       _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB,
7058       _is_keyPADMUL, _is_keyPADDIV;
7059 
7060     //@}
7061     //---------------------------
7062     //
7063     //! \name Plugins
7064     //@{
7065     //---------------------------
7066 
7067 #ifdef cimgdisplay_plugin
7068 #include cimgdisplay_plugin
7069 #endif
7070 #ifdef cimgdisplay_plugin1
7071 #include cimgdisplay_plugin1
7072 #endif
7073 #ifdef cimgdisplay_plugin2
7074 #include cimgdisplay_plugin2
7075 #endif
7076 #ifdef cimgdisplay_plugin3
7077 #include cimgdisplay_plugin3
7078 #endif
7079 #ifdef cimgdisplay_plugin4
7080 #include cimgdisplay_plugin4
7081 #endif
7082 #ifdef cimgdisplay_plugin5
7083 #include cimgdisplay_plugin5
7084 #endif
7085 #ifdef cimgdisplay_plugin6
7086 #include cimgdisplay_plugin6
7087 #endif
7088 #ifdef cimgdisplay_plugin7
7089 #include cimgdisplay_plugin7
7090 #endif
7091 #ifdef cimgdisplay_plugin8
7092 #include cimgdisplay_plugin8
7093 #endif
7094 
7095     //@}
7096     //--------------------------------------------------------
7097     //
7098     //! \name Constructors / Destructor / Instance Management
7099     //@{
7100     //--------------------------------------------------------
7101 
7102     //! Destructor.
7103     /**
7104        \note If the associated window is visible on the screen, it is closed by the call to the destructor.
7105     **/
7106     ~CImgDisplay() {
7107       assign();
7108       delete[] _keys;
7109       delete[] _released_keys;
7110     }
7111 
7112     //! Construct an empty display.
7113     /**
7114        \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until
7115        display of valid data is performed.
7116        \par Example
7117        \code
7118        CImgDisplay disp;  // Does actually nothing.
7119        ...
7120        disp.display(img); // Construct new window and display image in it.
7121        \endcode
7122     **/
7123     CImgDisplay():
7124       _width(0),_height(0),_normalization(0),
7125       _min(0),_max(0),
7126       _is_fullscreen(false),
7127       _title(0),
7128       _window_width(0),_window_height(0),_button(0),
7129       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
7130       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
7131       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
7132       assign();
7133     }
7134 
7135     //! Construct a display with specified dimensions.
7136     /** \param width Window width.
7137         \param height Window height.
7138         \param title Window title.
7139         \param normalization Normalization type
7140         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
7141         \param is_fullscreen Tells if fullscreen mode is enabled.
7142         \param is_closed Tells if associated window is initially visible or not.
7143         \note A black background is initially displayed on the associated window.
7144     **/
7145     CImgDisplay(const unsigned int width, const unsigned int height,
7146                 const char *const title=0, const unsigned int normalization=3,
7147                 const bool is_fullscreen=false, const bool is_closed=false):
7148       _width(0),_height(0),_normalization(0),
7149       _min(0),_max(0),
7150       _is_fullscreen(false),
7151       _title(0),
7152       _window_width(0),_window_height(0),_button(0),
7153       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
7154       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
7155       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
7156       assign(width,height,title,normalization,is_fullscreen,is_closed);
7157     }
7158 
7159     //! Construct a display from an image.
7160     /** \param img Image used as a model to create the window.
7161         \param title Window title.
7162         \param normalization Normalization type
7163         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
7164         \param is_fullscreen Tells if fullscreen mode is enabled.
7165         \param is_closed Tells if associated window is initially visible or not.
7166         \note The pixels of the input image are initially displayed on the associated window.
7167     **/
7168     template<typename T>
7169     explicit CImgDisplay(const CImg<T>& img,
7170                          const char *const title=0, const unsigned int normalization=3,
7171                          const bool is_fullscreen=false, const bool is_closed=false):
7172       _width(0),_height(0),_normalization(0),
7173       _min(0),_max(0),
7174       _is_fullscreen(false),
7175       _title(0),
7176       _window_width(0),_window_height(0),_button(0),
7177       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
7178       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
7179       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
7180       assign(img,title,normalization,is_fullscreen,is_closed);
7181     }
7182 
7183     //! Construct a display from an image list.
7184     /** \param list The images list to display.
7185         \param title Window title.
7186         \param normalization Normalization type
7187         (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
7188         \param is_fullscreen Tells if fullscreen mode is enabled.
7189         \param is_closed Tells if associated window is initially visible or not.
7190         \note All images of the list, appended along the X-axis, are initially displayed on the associated window.
7191     **/
7192     template<typename T>
7193     explicit CImgDisplay(const CImgList<T>& list,
7194                          const char *const title=0, const unsigned int normalization=3,
7195                          const bool is_fullscreen=false, const bool is_closed=false):
7196       _width(0),_height(0),_normalization(0),
7197       _min(0),_max(0),
7198       _is_fullscreen(false),
7199       _title(0),
7200       _window_width(0),_window_height(0),_button(0),
7201       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
7202       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
7203       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
7204       assign(list,title,normalization,is_fullscreen,is_closed);
7205     }
7206 
7207     //! Construct a display as a copy of an existing one.
7208     /**
7209         \param disp Display instance to copy.
7210         \note The pixel buffer of the input window is initially displayed on the associated window.
7211     **/
7212     CImgDisplay(const CImgDisplay& disp):
7213       _width(0),_height(0),_normalization(0),
7214       _min(0),_max(0),
7215       _is_fullscreen(false),
7216       _title(0),
7217       _window_width(0),_window_height(0),_button(0),
7218       _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
7219       _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
7220       _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
7221       assign(disp);
7222     }
7223 
7224     //! Take a screenshot.
7225     /**
7226        \param[out] img Output screenshot. Can be empty on input
7227     **/
7228     template<typename T>
7229     static void screenshot(CImg<T>& img) {
7230       return screenshot(0,0,cimg::type<int>::max(),cimg::type<int>::max(),img);
7231     }
7232 
7233 #if cimg_display==0
7234 
7235     static void _no_display_exception() {
7236       throw CImgDisplayException("CImgDisplay(): No display available.");
7237     }
7238 
7239     //! Destructor - Empty constructor \inplace.
7240     /**
7241        \note Replace the current instance by an empty display.
7242     **/
7243     CImgDisplay& assign() {
7244       return flush();
7245     }
7246 
7247     //! Construct a display with specified dimensions \inplace.
7248     /**
7249     **/
7250     CImgDisplay& assign(const unsigned int width, const unsigned int height,
7251                         const char *const title=0, const unsigned int normalization=3,
7252                         const bool is_fullscreen=false, const bool is_closed=false) {
7253       cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
7254       _no_display_exception();
7255       return assign();
7256     }
7257 
7258     //! Construct a display from an image \inplace.
7259     /**
7260     **/
7261     template<typename T>
7262     CImgDisplay& assign(const CImg<T>& img,
7263                         const char *const title=0, const unsigned int normalization=3,
7264                         const bool is_fullscreen=false, const bool is_closed=false) {
7265       _no_display_exception();
7266       return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
7267     }
7268 
7269     //! Construct a display from an image list \inplace.
7270     /**
7271     **/
7272     template<typename T>
7273     CImgDisplay& assign(const CImgList<T>& list,
7274                         const char *const title=0, const unsigned int normalization=3,
7275                         const bool is_fullscreen=false, const bool is_closed=false) {
7276       _no_display_exception();
7277       return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
7278     }
7279 
7280     //! Construct a display as a copy of another one \inplace.
7281     /**
7282     **/
7283     CImgDisplay& assign(const CImgDisplay &disp) {
7284       _no_display_exception();
7285       return assign(disp._width,disp._height);
7286     }
7287 
7288 #endif
7289 
7290     //! Return a reference to an empty display.
7291     /**
7292        \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&)
7293        must have a default value.
7294        \par Example
7295        \code
7296        void foo(CImgDisplay& disp=CImgDisplay::empty());
7297        \endcode
7298     **/
7299     static CImgDisplay& empty() {
7300       static CImgDisplay _empty;
7301       return _empty.assign();
7302     }
7303 
7304     //! Return a reference to an empty display \const.
7305     static const CImgDisplay& const_empty() {
7306       static const CImgDisplay _empty;
7307       return _empty;
7308     }
7309 
7310 #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,480,-85,false), \
7311                                  CImgDisplay::_fitscreen(dx,dy,dz,480,-85,true)
7312     static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
7313                                    const int dmin, const int dmax, const bool return_y) {
7314       const unsigned int _nw = dx + (dz>1?dz:0), _nh = dy + (dz>1?dz:0);
7315       unsigned int nw = _nw?_nw:1, nh = _nh?_nh:1;
7316       const unsigned int
7317         sw = (unsigned int)CImgDisplay::screen_width(),
7318         sh = (unsigned int)CImgDisplay::screen_height(),
7319         mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin,
7320         mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin,
7321         Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax,
7322         Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax;
7323       if (nw<mw) { nh = nh*mw/nw; nh+=(nh==0); nw = mw; }
7324       if (nh<mh) { nw = nw*mh/nh; nw+=(nw==0); nh = mh; }
7325       if (nw>Mw) { nh = nh*Mw/nw; nh+=(nh==0); nw = Mw; }
7326       if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0); nh = Mh; }
7327       if (nw<mw) nw = mw;
7328       if (nh<mh) nh = mh;
7329       return return_y?nh:nw;
7330     }
7331 
7332     //@}
7333     //------------------------------------------
7334     //
7335     //! \name Overloaded Operators
7336     //@{
7337     //------------------------------------------
7338 
7339     //! Display image on associated window.
7340     /**
7341        \note <tt>disp = img</tt> is equivalent to <tt>disp.display(img)</tt>.
7342     **/
7343     template<typename t>
7344     CImgDisplay& operator=(const CImg<t>& img) {
7345       return display(img);
7346     }
7347 
7348     //! Display list of images on associated window.
7349     /**
7350        \note <tt>disp = list</tt> is equivalent to <tt>disp.display(list)</tt>.
7351     **/
7352     template<typename t>
7353     CImgDisplay& operator=(const CImgList<t>& list) {
7354       return display(list);
7355     }
7356 
7357     //! Construct a display as a copy of another one \inplace.
7358     /**
7359        \note Equivalent to assign(const CImgDisplay&).
7360      **/
7361     CImgDisplay& operator=(const CImgDisplay& disp) {
7362       return assign(disp);
7363     }
7364 
7365     //! Return \c false if display is empty, \c true otherwise.
7366     /**
7367        \note <tt>if (disp) { ... }</tt> is equivalent to <tt>if (!disp.is_empty()) { ... }</tt>.
7368     **/
7369     operator bool() const {
7370       return !is_empty();
7371     }
7372 
7373     //@}
7374     //------------------------------------------
7375     //
7376     //! \name Instance Checking
7377     //@{
7378     //------------------------------------------
7379 
7380     //! Return \c true if display is empty, \c false otherwise.
7381     /**
7382     **/
7383     bool is_empty() const {
7384       return !(_width && _height);
7385     }
7386 
7387     //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise.
7388     /**
7389        \note
7390        - When a user physically closes the associated window, the display is set to closed.
7391        - A closed display is not destroyed. Its associated window can be show again on the screen using show().
7392     **/
7393     bool is_closed() const {
7394       return _is_closed;
7395     }
7396 
7397     //! Return \c true if associated window has been resized on the screen, \c false otherwise.
7398     /**
7399     **/
7400     bool is_resized() const {
7401       return _is_resized;
7402     }
7403 
7404     //! Return \c true if associated window has been moved on the screen, \c false otherwise.
7405     /**
7406     **/
7407     bool is_moved() const {
7408       return _is_moved;
7409     }
7410 
7411     //! Return \c true if any event has occured on the associated window, \c false otherwise.
7412     /**
7413     **/
7414     bool is_event() const {
7415       return _is_event;
7416     }
7417 
7418     //! Return \c true if current display is in fullscreen mode, \c false otherwise.
7419     /**
7420     **/
7421     bool is_fullscreen() const {
7422       return _is_fullscreen;
7423     }
7424 
7425     //! Return \c true if any key is being pressed on the associated window, \c false otherwise.
7426     /**
7427        \note The methods below do the same only for specific keys.
7428     **/
7429     bool is_key() const {
7430       return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
7431         _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
7432         _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
7433         _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
7434         _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
7435         _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
7436         _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
7437         _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
7438         _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
7439         _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
7440         _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
7441         _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
7442         _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
7443         _is_keyK || _is_keyL || _is_keyENTER ||
7444         _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
7445         _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
7446         _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
7447         _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
7448         _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
7449         _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
7450         _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
7451         _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
7452         _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
7453         _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
7454         _is_keyPADMUL || _is_keyPADDIV;
7455     }
7456 
7457     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
7458     /**
7459        \param keycode Keycode to test.
7460        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7461        your code stay portable (see cimg::keyESC).
7462        \par Example
7463        \code
7464        CImgDisplay disp(400,400);
7465        while (!disp.is_closed()) {
7466          if (disp.key(cimg::keyTAB)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'.
7467          disp.wait();
7468        }
7469        \endcode
7470     **/
7471     bool is_key(const unsigned int keycode) const {
7472 #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
7473       _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
7474       _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
7475       _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
7476       _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
7477       _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
7478       _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
7479       _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
7480       _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
7481       _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
7482       _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
7483       _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
7484       _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
7485       _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
7486       _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
7487       _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
7488       _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
7489       _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
7490       _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
7491       _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
7492       _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
7493       _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
7494       _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
7495       _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
7496       _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
7497       _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
7498       return false;
7499     }
7500 
7501     //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
7502     /**
7503        \param keycode C-string containing the keycode label of the key to test.
7504        \note Use it when the key you want to test can be dynamically set by the user.
7505        \par Example
7506        \code
7507        CImgDisplay disp(400,400);
7508        const char *const keycode = "TAB";
7509        while (!disp.is_closed()) {
7510          if (disp.is_key(keycode)) { ... }  // Equivalent to 'if (disp.is_keyTAB())'.
7511          disp.wait();
7512        }
7513        \endcode
7514     **/
7515     bool& is_key(const char *const keycode) {
7516       static bool f = false;
7517       f = false;
7518 #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
7519       _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
7520       _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
7521       _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
7522       _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
7523       _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
7524       _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
7525       _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
7526       _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
7527       _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
7528       _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
7529       _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
7530       _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
7531       _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
7532       _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
7533       _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
7534       _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
7535       _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
7536       _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
7537       _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
7538       _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
7539       _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
7540       _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
7541       _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
7542       _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
7543       _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
7544       return f;
7545     }
7546 
7547     //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise.
7548     /**
7549        \param keycodes_sequence Buffer of keycodes to test.
7550        \param length Number of keys in the \c keycodes_sequence buffer.
7551        \param remove_sequence Tells if the key sequence must be removed from the key history, if found.
7552        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7553        your code stay portable (see cimg::keyESC).
7554        \par Example
7555        \code
7556        CImgDisplay disp(400,400);
7557        const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD };
7558        while (!disp.is_closed()) {
7559          if (disp.is_key_sequence(key_seq,2)) { ... }  // Test for the 'CTRL+D' keyboard event.
7560          disp.wait();
7561        }
7562        \endcode
7563     **/
7564     bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length,
7565                          const bool remove_sequence=false) {
7566       if (keycodes_sequence && length) {
7567         const unsigned int
7568           *const ps_end = keycodes_sequence + length - 1,
7569           *const pk_end = (unsigned int*)_keys + 1 + 128 - length,
7570           k = *ps_end;
7571         for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
7572           if (*(pk++)==k) {
7573             bool res = true;
7574             const unsigned int *ps = ps_end, *pk2 = pk;
7575             for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
7576             if (res) {
7577               if (remove_sequence) std::memset((void*)(pk - 1),0,sizeof(unsigned int)*length);
7578               return true;
7579             }
7580           }
7581         }
7582       }
7583       return false;
7584     }
7585 
7586 #define _cimg_iskey_def(k) \
7587     bool is_key##k() const { \
7588       return _is_key##k; \
7589     }
7590 
7591     //! Return \c true if the \c ESC key is being pressed on the associated window, \c false otherwise.
7592     /**
7593        \note Similar methods exist for all keys managed by \CImg (see cimg::keyESC).
7594     **/
7595     _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
7596     _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
7597     _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
7598     _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
7599     _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
7600     _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
7601     _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
7602     _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
7603     _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
7604     _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
7605     _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
7606     _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
7607     _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
7608     _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
7609     _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
7610     _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
7611     _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
7612     _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
7613     _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
7614     _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
7615     _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
7616     _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
7617     _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
7618     _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
7619     _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
7620 
7621     //@}
7622     //------------------------------------------
7623     //
7624     //! \name Instance Characteristics
7625     //@{
7626     //------------------------------------------
7627 
7628 #if cimg_display==0
7629 
7630     //! Return width of the screen (current resolution along the X-axis).
7631     /**
7632     **/
7633     static int screen_width() {
7634       _no_display_exception();
7635       return 0;
7636     }
7637 
7638     //! Return height of the screen (current resolution along the Y-axis).
7639     /**
7640     **/
7641     static int screen_height() {
7642       _no_display_exception();
7643       return 0;
7644     }
7645 
7646 #endif
7647 
7648     //! Return display width.
7649     /**
7650        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
7651        may be different from the actual width of the associated window.
7652     **/
7653     int width() const {
7654       return (int)_width;
7655     }
7656 
7657     //! Return display height.
7658     /**
7659        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
7660        may be different from the actual height of the associated window.
7661     **/
7662     int height() const {
7663       return (int)_height;
7664     }
7665 
7666     //! Return normalization type of the display.
7667     /**
7668        The normalization type tells about how the values of an input image are normalized by the CImgDisplay to be
7669        correctly displayed. The range of values for pixels displayed on screen is <tt>[0,255]</tt>.
7670        If the range of values of the data to display is different, a normalization may be required for displaying
7671        the data in a correct way. The normalization type can be one of:
7672        - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the
7673        CImgDisplay instance have values in range <tt>[0,255]</tt>.
7674        - \c 1: Value normalization is always performed (this is the default behavior).
7675        Before displaying an input image, its values will be (virtually) stretched
7676        in range <tt>[0,255]</tt>, so that the contrast of the displayed pixels will be maximum.
7677        Use this mode for images whose minimum and maximum values are not prescribed to known values
7678        (e.g. float-valued images).
7679        Note that when normalized versions of images are computed for display purposes, the actual values of these
7680        images are not modified.
7681        - \c 2: Value normalization is performed once (on the first image display), then the same normalization
7682        coefficients are kept for next displayed frames.
7683        - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types,
7684        the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then
7685        for <tt>unsigned char</tt>).
7686        For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image
7687        data instead.
7688     **/
7689     unsigned int normalization() const {
7690       return _normalization;
7691     }
7692 
7693     //! Return title of the associated window as a C-string.
7694     /**
7695        \note Window title may be not visible, depending on the used window manager or if the current display is
7696        in fullscreen mode.
7697     **/
7698     const char *title() const {
7699       return _title?_title:"";
7700     }
7701 
7702     //! Return width of the associated window.
7703     /**
7704        \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
7705        may be different from the actual width of the associated window.
7706     **/
7707     int window_width() const {
7708       return (int)_window_width;
7709     }
7710 
7711     //! Return height of the associated window.
7712     /**
7713        \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
7714        may be different from the actual height of the associated window.
7715     **/
7716     int window_height() const {
7717       return (int)_window_height;
7718     }
7719 
7720     //! Return X-coordinate of the associated window.
7721     /**
7722        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
7723     **/
7724     int window_x() const {
7725       return _window_x;
7726     }
7727 
7728     //! Return Y-coordinate of the associated window.
7729     /**
7730        \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
7731     **/
7732     int window_y() const {
7733       return _window_y;
7734     }
7735 
7736     //! Return X-coordinate of the mouse pointer.
7737     /**
7738        \note
7739        - If the mouse pointer is outside window area, \c -1 is returned.
7740        - Otherwise, the returned value is in the range [0,width()-1].
7741     **/
7742     int mouse_x() const {
7743       return _mouse_x;
7744     }
7745 
7746     //! Return Y-coordinate of the mouse pointer.
7747     /**
7748        \note
7749        - If the mouse pointer is outside window area, \c -1 is returned.
7750        - Otherwise, the returned value is in the range [0,height()-1].
7751     **/
7752     int mouse_y() const {
7753       return _mouse_y;
7754     }
7755 
7756     //! Return current state of the mouse buttons.
7757     /**
7758        \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned
7759        value is set:
7760        - bit \c 0 (value \c 0x1): State of the left mouse button.
7761        - bit \c 1 (value \c 0x2): State of the right mouse button.
7762        - bit \c 2 (value \c 0x4): State of the middle mouse button.
7763 
7764        Several bits can be activated if more than one button are pressed at the same time.
7765        \par Example
7766        \code
7767        CImgDisplay disp(400,400);
7768        while (!disp.is_closed()) {
7769          if (disp.button()&1) { // Left button clicked.
7770            ...
7771          }
7772          if (disp.button()&2) { // Right button clicked.
7773            ...
7774          }
7775          if (disp.button()&4) { // Middle button clicked.
7776            ...
7777          }
7778          disp.wait();
7779        }
7780        \endcode
7781     **/
7782     unsigned int button() const {
7783       return _button;
7784     }
7785 
7786     //! Return current state of the mouse wheel.
7787     /**
7788        \note
7789        - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled
7790        forward or backward.
7791        - Scrolling the wheel forward add \c 1 to the wheel value.
7792        - Scrolling the wheel backward substract \c 1 to the wheel value.
7793        - The returned value cumulates the number of forward of backward scrolls since the creation of the display,
7794        or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset
7795        the wheel counter when an action has been performed regarding the current wheel value.
7796        Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done
7797        (as many in forward as in backward directions).
7798        \par Example
7799        \code
7800        CImgDisplay disp(400,400);
7801        while (!disp.is_closed()) {
7802          if (disp.wheel()) {
7803            int counter = disp.wheel();  // Read the state of the mouse wheel.
7804            ...                          // Do what you want with 'counter'.
7805            disp.set_wheel();            // Reset the wheel value to 0.
7806          }
7807          disp.wait();
7808        }
7809        \endcode
7810     **/
7811     int wheel() const {
7812       return _wheel;
7813     }
7814 
7815     //! Return one entry from the pressed keys history.
7816     /**
7817        \param pos Indice to read from the pressed keys history (indice \c 0 corresponds to latest entry).
7818        \return Keycode of a pressed key or \c 0 for a released key.
7819        \note
7820        - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed,
7821        its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead.
7822        This means that up to the 64 last pressed keys may be read from the pressed keys history.
7823        When a new value is stored, the pressed keys history is shifted so that the latest entry is always
7824        stored at position \c 0.
7825        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7826        your code stay portable (see cimg::keyESC).
7827     **/
7828     unsigned int key(const unsigned int pos=0) const {
7829       return pos<128?_keys[pos]:0;
7830     }
7831 
7832     //! Return one entry from the released keys history.
7833     /**
7834        \param pos Indice to read from the released keys history (indice \c 0 corresponds to latest entry).
7835        \return Keycode of a released key or \c 0 for a pressed key.
7836        \note
7837        - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released,
7838        its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead.
7839        This means that up to the 64 last released keys may be read from the released keys history.
7840        When a new value is stored, the released keys history is shifted so that the latest entry is always
7841        stored at position \c 0.
7842        - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7843        your code stay portable (see cimg::keyESC).
7844     **/
7845     unsigned int released_key(const unsigned int pos=0) const {
7846       return pos<128?_released_keys[pos]:0;
7847     }
7848 
7849     //! Return keycode corresponding to the specified string.
7850     /**
7851        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
7852        your code stay portable (see cimg::keyESC).
7853        \par Example
7854        \code
7855        const unsigned int keyTAB = CImgDisplay::keycode("TAB");  // Return cimg::keyTAB.
7856        \endcode
7857     **/
7858     static unsigned int keycode(const char *const keycode) {
7859 #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
7860       _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
7861       _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
7862       _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
7863       _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
7864       _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
7865       _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
7866       _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
7867       _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
7868       _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
7869       _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
7870       _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
7871       _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
7872       _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
7873       _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
7874       _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
7875       _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
7876       _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
7877       _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
7878       _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
7879       _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
7880       _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
7881       _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
7882       _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
7883       _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
7884       _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
7885       return 0;
7886     }
7887 
7888     //! Return the current refresh rate, in frames per second.
7889     /**
7890        \note Returns a significant value when the current instance is used to display successive frames.
7891        It measures the delay between successive calls to frames_per_second().
7892     **/
7893     float frames_per_second() {
7894       if (!_fps_timer) _fps_timer = cimg::time();
7895       const float delta = (cimg::time() - _fps_timer)/1000.0f;
7896       ++_fps_frames;
7897       if (delta>=1) {
7898         _fps_fps = _fps_frames/delta;
7899         _fps_frames = 0;
7900         _fps_timer = cimg::time();
7901       }
7902       return _fps_fps;
7903     }
7904 
7905     //@}
7906     //---------------------------------------
7907     //
7908     //! \name Window Manipulation
7909     //@{
7910     //---------------------------------------
7911 
7912 #if cimg_display==0
7913 
7914     //! Display image on associated window.
7915     /**
7916        \param img Input image to display.
7917        \note This method returns immediately.
7918     **/
7919     template<typename T>
7920     CImgDisplay& display(const CImg<T>& img) {
7921       return assign(img);
7922     }
7923 
7924 #endif
7925 
7926     //! Display list of images on associated window.
7927     /**
7928        \param list List of images to display.
7929        \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c).
7930        \param align Relative position of aligned images when displaying lists with images of different sizes
7931        (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right).
7932        \note This method returns immediately.
7933     **/
7934     template<typename T>
7935     CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
7936       if (list._width==1) {
7937         const CImg<T>& img = list[0];
7938         if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img);
7939       }
7940       CImgList<typename CImg<T>::ucharT> visu(list._width);
7941       unsigned int dims = 0;
7942       cimglist_for(list,l) {
7943         const CImg<T>& img = list._data[l];
7944         img.__get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2,
7945                          (img._depth - 1)/2).move_to(visu[l]);
7946         dims = std::max(dims,visu[l]._spectrum);
7947       }
7948       cimglist_for(list,l) if (visu[l]._spectrum<dims) visu[l].resize(-100,-100,-100,dims,1);
7949       visu.get_append(axis,align).display(*this);
7950       return *this;
7951     }
7952 
7953 #if cimg_display==0
7954 
7955     //! Show (closed) associated window on the screen.
7956     /**
7957        \note
7958        - Force the associated window of a display to be visible on the screen, even if it has been closed before.
7959        - Using show() on a visible display does nothing.
7960     **/
7961     CImgDisplay& show() {
7962       return assign();
7963     }
7964 
7965     //! Close (visible) associated window and make it disappear from the screen.
7966     /**
7967        \note
7968        - A closed display only means the associated window is not visible anymore. This does not mean the display has
7969        been destroyed.
7970        Use show() to make the associated window reappear.
7971        - Using close() on a closed display does nothing.
7972     **/
7973     CImgDisplay& close() {
7974       return assign();
7975     }
7976 
7977     //! Move associated window to a new location.
7978     /**
7979        \param pos_x X-coordinate of the new window location.
7980        \param pos_y Y-coordinate of the new window location.
7981        \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown
7982        nevertheless).
7983     **/
7984     CImgDisplay& move(const int pos_x, const int pos_y) {
7985       return assign(pos_x,pos_y);
7986     }
7987 
7988 #endif
7989 
7990     //! Resize display to the size of the associated window.
7991     /**
7992        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
7993        \note
7994        - Calling this method ensures that width() and window_width() become equal, as well as height() and
7995        window_height().
7996        - The associated window is also resized to specified dimensions.
7997     **/
7998     CImgDisplay& resize(const bool force_redraw=true) {
7999       resize(window_width(),window_height(),force_redraw);
8000       return *this;
8001     }
8002 
8003 #if cimg_display==0
8004 
8005     //! Resize display to the specified size.
8006     /**
8007        \param width Requested display width.
8008        \param height Requested display height.
8009        \param force_redraw Tells if the previous window content must be updated and refreshed as well.
8010        \note The associated window is also resized to specified dimensions.
8011     **/
8012     CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
8013       return assign(width,height,0,3,force_redraw);
8014     }
8015 
8016 #endif
8017 
8018     //! Resize display to the size of an input image.
8019     /**
8020        \param img Input image to take size from.
8021        \param force_redraw Tells if the previous window content must be resized and updated as well.
8022        \note
8023        - Calling this method ensures that width() and <tt>img.width()</tt> become equal, as well as height() and
8024        <tt>img.height()</tt>.
8025        - The associated window is also resized to specified dimensions.
8026     **/
8027     template<typename T>
8028     CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
8029       return resize(img._width,img._height,force_redraw);
8030     }
8031 
8032     //! Resize display to the size of another CImgDisplay instance.
8033     /**
8034        \param disp Input display to take size from.
8035        \param force_redraw Tells if the previous window content must be resized and updated as well.
8036        \note
8037        - Calling this method ensures that width() and <tt>disp.width()</tt> become equal, as well as height() and
8038        <tt>disp.height()</tt>.
8039        - The associated window is also resized to specified dimensions.
8040     **/
8041     CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
8042       return resize(disp.width(),disp.height(),force_redraw);
8043     }
8044 
8045     // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
8046     template<typename t, typename T>
8047     static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
8048                                t *ptrd, const unsigned int wd, const unsigned int hd) {
8049       unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd + 1], *poffx, *poffy;
8050       float s, curr, old;
8051       s = (float)ws/wd;
8052       poffx = offx; curr = 0; for (unsigned int x = 0; x<wd; ++x) {
8053         old = curr; curr+=s; *(poffx++) = (unsigned int)curr - (unsigned int)old;
8054       }
8055       s = (float)hs/hd;
8056       poffy = offy; curr = 0; for (unsigned int y = 0; y<hd; ++y) {
8057         old = curr; curr+=s; *(poffy++) = ws*((unsigned int)curr - (unsigned int)old);
8058       }
8059       *poffy = 0;
8060       poffy = offy;
8061       for (unsigned int y = 0; y<hd; ) {
8062         const T *ptr = ptrs;
8063         poffx = offx;
8064         for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
8065         ++y;
8066         unsigned int dy = *(poffy++);
8067         for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poffy++)) {}
8068         ptrs+=dy;
8069       }
8070       delete[] offx; delete[] offy;
8071     }
8072 
8073     //! Set normalization type.
8074     /**
8075        \param normalization New normalization mode.
8076     **/
8077     CImgDisplay& set_normalization(const unsigned int normalization) {
8078       _normalization = normalization;
8079       _min = _max = 0;
8080       return *this;
8081     }
8082 
8083 #if cimg_display==0
8084 
8085     //! Set title of the associated window.
8086     /**
8087        \param format C-string containing the format of the title, as with <tt>std::printf()</tt>.
8088        \warning As the first argument is a format string, it is highly recommended to write
8089        \code
8090        disp.set_title("%s",window_title);
8091        \endcode
8092        instead of
8093        \code
8094        disp.set_title(window_title);
8095        \endcode
8096        if \c window_title can be arbitrary, to prevent nasty memory access.
8097     **/
8098     CImgDisplay& set_title(const char *const format, ...) {
8099       return assign(0,0,format);
8100     }
8101 
8102 #endif
8103 
8104     //! Enable or disable fullscreen mode.
8105     /**
8106        \param is_fullscreen Tells is the fullscreen mode must be activated or not.
8107        \param force_redraw Tells if the previous window content must be displayed as well.
8108        \note
8109        - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the
8110        current display is not modified.
8111        - The screen resolution may be switched to fit the associated window size and ensure it appears the largest
8112        as possible.
8113        For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen
8114        resolution change (requires the X11 extensions to be enabled).
8115     **/
8116     CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
8117       if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
8118       return toggle_fullscreen(force_redraw);
8119     }
8120 
8121 #if cimg_display==0
8122 
8123     //! Toggle fullscreen mode.
8124     /**
8125        \param force_redraw Tells if the previous window content must be displayed as well.
8126        \note Enable fullscreen mode if it was not enabled, and disable it otherwise.
8127     **/
8128     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
8129       return assign(_width,_height,0,3,force_redraw);
8130     }
8131 
8132     //! Show mouse pointer.
8133     /**
8134        \note Depending on the window manager behavior, this method may not succeed
8135        (no exceptions are thrown nevertheless).
8136     **/
8137     CImgDisplay& show_mouse() {
8138       return assign();
8139     }
8140 
8141     //! Hide mouse pointer.
8142     /**
8143        \note Depending on the window manager behavior, this method may not succeed
8144        (no exceptions are thrown nevertheless).
8145     **/
8146     CImgDisplay& hide_mouse() {
8147       return assign();
8148     }
8149 
8150     //! Move mouse pointer to a specified location.
8151     /**
8152        \note Depending on the window manager behavior, this method may not succeed
8153        (no exceptions are thrown nevertheless).
8154     **/
8155     CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
8156       return assign(pos_x,pos_y);
8157     }
8158 
8159 #endif
8160 
8161     //! Simulate a mouse button release event.
8162     /**
8163        \note All mouse buttons are considered released at the same time.
8164     **/
8165     CImgDisplay& set_button() {
8166       _button = 0;
8167       _is_event = true;
8168 #if cimg_display==1
8169       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8170 #elif cimg_display==2
8171       SetEvent(cimg::Win32_attr().wait_event);
8172 #endif
8173       return *this;
8174     }
8175 
8176     //! Simulate a mouse button press or release event.
8177     /**
8178        \param button Buttons event code, where each button is associated to a single bit.
8179        \param is_pressed Tells if the mouse button is considered as pressed or released.
8180     **/
8181     CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
8182       const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U;
8183       if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
8184       _is_event = buttoncode?true:false;
8185       if (buttoncode) {
8186 #if cimg_display==1
8187         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8188 #elif cimg_display==2
8189         SetEvent(cimg::Win32_attr().wait_event);
8190 #endif
8191       }
8192       return *this;
8193     }
8194 
8195     //! Flush all mouse wheel events.
8196     /**
8197        \note Make wheel() to return \c 0, if called afterwards.
8198     **/
8199     CImgDisplay& set_wheel() {
8200       _wheel = 0;
8201       _is_event = true;
8202 #if cimg_display==1
8203       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8204 #elif cimg_display==2
8205       SetEvent(cimg::Win32_attr().wait_event);
8206 #endif
8207       return *this;
8208     }
8209 
8210     //! Simulate a wheel event.
8211     /**
8212        \param amplitude Amplitude of the wheel scrolling to simulate.
8213        \note Make wheel() to return \c amplitude, if called afterwards.
8214     **/
8215     CImgDisplay& set_wheel(const int amplitude) {
8216       _wheel+=amplitude;
8217       _is_event = amplitude?true:false;
8218       if (amplitude) {
8219 #if cimg_display==1
8220         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8221 #elif cimg_display==2
8222         SetEvent(cimg::Win32_attr().wait_event);
8223 #endif
8224       }
8225       return *this;
8226     }
8227 
8228     //! Flush all key events.
8229     /**
8230        \note Make key() to return \c 0, if called afterwards.
8231     **/
8232     CImgDisplay& set_key() {
8233       std::memset((void*)_keys,0,128*sizeof(unsigned int));
8234       std::memset((void*)_released_keys,0,128*sizeof(unsigned int));
8235       _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 =
8236         _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 =
8237         _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT =
8238         _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY =
8239         _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK =
8240         _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL =
8241         _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
8242         _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE =
8243         _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN =
8244         _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 =
8245         _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL =
8246         _is_keyPADDIV = false;
8247       _is_event = true;
8248 #if cimg_display==1
8249       pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8250 #elif cimg_display==2
8251       SetEvent(cimg::Win32_attr().wait_event);
8252 #endif
8253       return *this;
8254     }
8255 
8256     //! Simulate a keyboard press/release event.
8257     /**
8258        \param keycode Keycode of the associated key.
8259        \param is_pressed Tells if the key is considered as pressed or released.
8260        \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
8261        your code stay portable (see cimg::keyESC).
8262     **/
8263     CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
8264 #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
8265       _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
8266       _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
8267       _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
8268       _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
8269       _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
8270       _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
8271       _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
8272       _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
8273       _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
8274       _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
8275       _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
8276       _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
8277       _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
8278       _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
8279       _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
8280       _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
8281       _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
8282       _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
8283       _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
8284       _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
8285       _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
8286       _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
8287       _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
8288       _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
8289       _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
8290       if (is_pressed) {
8291         if (*_keys)
8292           std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
8293         *_keys = keycode;
8294         if (*_released_keys) {
8295           std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
8296           *_released_keys = 0;
8297         }
8298       } else {
8299         if (*_keys) {
8300           std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
8301           *_keys = 0;
8302         }
8303         if (*_released_keys)
8304           std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
8305         *_released_keys = keycode;
8306       }
8307       _is_event = keycode?true:false;
8308       if (keycode) {
8309 #if cimg_display==1
8310         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8311 #elif cimg_display==2
8312         SetEvent(cimg::Win32_attr().wait_event);
8313 #endif
8314       }
8315       return *this;
8316     }
8317 
8318     //! Flush all display events.
8319     /**
8320        \note Remove all passed events from the current display.
8321     **/
8322     CImgDisplay& flush() {
8323       set_key().set_button().set_wheel();
8324       _is_resized = _is_moved = _is_event = false;
8325       _fps_timer = _fps_frames = _timer = 0;
8326       _fps_fps = 0;
8327       return *this;
8328     }
8329 
8330     //! Wait for any user event occuring on the current display.
8331     CImgDisplay& wait() {
8332       wait(*this);
8333       return *this;
8334     }
8335 
8336     //! Wait for a given number of milliseconds since the last call to wait().
8337     /**
8338        \param milliseconds Number of milliseconds to wait for.
8339        \note Similar to cimg::wait().
8340     **/
8341     CImgDisplay& wait(const unsigned int milliseconds) {
8342       cimg::_wait(milliseconds,_timer);
8343       return *this;
8344     }
8345 
8346     //! Wait for any event occuring on the display \c disp1.
8347     static void wait(CImgDisplay& disp1) {
8348       disp1._is_event = false;
8349       while (!disp1._is_closed && !disp1._is_event) wait_all();
8350     }
8351 
8352     //! Wait for any event occuring either on the display \c disp1 or \c disp2.
8353     static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
8354       disp1._is_event = disp2._is_event = false;
8355       while ((!disp1._is_closed || !disp2._is_closed) &&
8356              !disp1._is_event && !disp2._is_event) wait_all();
8357     }
8358 
8359     //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3.
8360     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
8361       disp1._is_event = disp2._is_event = disp3._is_event = false;
8362       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
8363              !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
8364     }
8365 
8366     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
8367     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
8368       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false;
8369       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
8370              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
8371     }
8372 
8373     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
8374     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4,
8375                      CImgDisplay& disp5) {
8376       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false;
8377       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
8378              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event)
8379         wait_all();
8380     }
8381 
8382     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
8383     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8384                      CImgDisplay& disp6) {
8385       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8386         disp6._is_event = false;
8387       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8388               !disp6._is_closed) &&
8389              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8390              !disp6._is_event) wait_all();
8391     }
8392 
8393     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
8394     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8395                      CImgDisplay& disp6, CImgDisplay& disp7) {
8396       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8397         disp6._is_event = disp7._is_event = false;
8398       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8399               !disp6._is_closed || !disp7._is_closed) &&
8400              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8401              !disp6._is_event && !disp7._is_event) wait_all();
8402     }
8403 
8404     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
8405     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8406                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
8407       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8408         disp6._is_event = disp7._is_event = disp8._is_event = false;
8409       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8410               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
8411              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8412              !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
8413     }
8414 
8415     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
8416     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8417                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
8418       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8419         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false;
8420       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8421               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
8422              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8423              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
8424     }
8425 
8426     //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
8427     static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
8428                      CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9,
8429                      CImgDisplay& disp10) {
8430       disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
8431         disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false;
8432       while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
8433               !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
8434              !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
8435              !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event)
8436         wait_all();
8437     }
8438 
8439 #if cimg_display==0
8440 
8441     //! Wait for any window event occuring in any opened CImgDisplay.
8442     static void wait_all() {
8443       return _no_display_exception();
8444     }
8445 
8446     //! Render image into internal display buffer.
8447     /**
8448        \param img Input image data to render.
8449        \note
8450        - Convert image data representation into the internal display buffer (architecture-dependent structure).
8451        - The content of the associated window is not modified, until paint() is called.
8452        - Should not be used for common CImgDisplay uses, since display() is more useful.
8453     **/
8454     template<typename T>
8455     CImgDisplay& render(const CImg<T>& img) {
8456       return assign(img);
8457     }
8458 
8459     //! Paint internal display buffer on associated window.
8460     /**
8461        \note
8462        - Update the content of the associated window with the internal display buffer, e.g. after a render() call.
8463        - Should not be used for common CImgDisplay uses, since display() is more useful.
8464     **/
8465     CImgDisplay& paint() {
8466       return assign();
8467     }
8468 
8469 
8470     //! Take a snapshot of the current screen content.
8471     /**
8472        \param x0 X-coordinate of the upper left corner.
8473        \param y0 Y-coordinate of the upper left corner.
8474        \param x1 X-coordinate of the lower right corner.
8475        \param y1 Y-coordinate of the lower right corner.
8476        \param[out] img Output screenshot. Can be empty on input
8477     **/
8478     template<typename T>
8479     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
8480       cimg::unused(x0,y0,x1,y1,&img);
8481       _no_display_exception();
8482     }
8483 
8484     //! Take a snapshot of the associated window content.
8485     /**
8486        \param[out] img Output snapshot. Can be empty on input.
8487     **/
8488     template<typename T>
8489     const CImgDisplay& snapshot(CImg<T>& img) const {
8490       cimg::unused(img);
8491       _no_display_exception();
8492       return *this;
8493     }
8494 #endif
8495 
8496     // X11-based implementation
8497     //--------------------------
8498 #if cimg_display==1
8499 
8500     Atom _wm_window_atom, _wm_protocol_atom;
8501     Window _window, _background_window;
8502     Colormap _colormap;
8503     XImage *_image;
8504     void *_data;
8505 #ifdef cimg_use_xshm
8506     XShmSegmentInfo *_shminfo;
8507 #endif
8508 
8509     static int screen_width() {
8510       Display *const dpy = cimg::X11_attr().display;
8511       int res = 0;
8512       if (!dpy) {
8513         Display *const _dpy = XOpenDisplay(0);
8514         if (!_dpy)
8515           throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
8516         res = DisplayWidth(_dpy,DefaultScreen(_dpy));
8517         XCloseDisplay(_dpy);
8518       } else {
8519 #ifdef cimg_use_xrandr
8520         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
8521           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
8522         else res = DisplayWidth(dpy,DefaultScreen(dpy));
8523 #else
8524         res = DisplayWidth(dpy,DefaultScreen(dpy));
8525 #endif
8526       }
8527       return res;
8528     }
8529 
8530     static int screen_height() {
8531       Display *const dpy = cimg::X11_attr().display;
8532       int res = 0;
8533       if (!dpy) {
8534         Display *const _dpy = XOpenDisplay(0);
8535         if (!_dpy)
8536           throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
8537         res = DisplayHeight(_dpy,DefaultScreen(_dpy));
8538         XCloseDisplay(_dpy);
8539       } else {
8540 #ifdef cimg_use_xrandr
8541         if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
8542           res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
8543         else res = DisplayHeight(dpy,DefaultScreen(dpy));
8544 #else
8545         res = DisplayHeight(dpy,DefaultScreen(dpy));
8546 #endif
8547       }
8548       return res;
8549     }
8550 
8551     static void wait_all() {
8552       if (!cimg::X11_attr().display) return;
8553       pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex);
8554       pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex);
8555       pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex);
8556     }
8557 
8558     void _handle_events(const XEvent *const pevent) {
8559       Display *const dpy = cimg::X11_attr().display;
8560       XEvent event = *pevent;
8561       switch (event.type) {
8562       case ClientMessage : {
8563         if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
8564             (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
8565           XUnmapWindow(cimg::X11_attr().display,_window);
8566           _is_closed = _is_event = true;
8567           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8568         }
8569       } break;
8570       case ConfigureNotify : {
8571         while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
8572         const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
8573         const int nx = event.xconfigure.x, ny = event.xconfigure.y;
8574         if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
8575           _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
8576           XResizeWindow(dpy,_window,_window_width,_window_height);
8577           _is_resized = _is_event = true;
8578           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8579         }
8580         if (nx!=_window_x || ny!=_window_y) {
8581           _window_x = nx; _window_y = ny; _is_moved = _is_event = true;
8582           pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8583         }
8584       } break;
8585       case Expose : {
8586         while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
8587         _paint(false);
8588         if (_is_fullscreen) {
8589           XWindowAttributes attr;
8590           XGetWindowAttributes(dpy,_window,&attr);
8591           while (attr.map_state!=IsViewable) XSync(dpy,0);
8592           XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
8593         }
8594       } break;
8595       case ButtonPress : {
8596         do {
8597           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
8598           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
8599           switch (event.xbutton.button) {
8600           case 1 : set_button(1); break;
8601           case 3 : set_button(2); break;
8602           case 2 : set_button(3); break;
8603           }
8604         } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
8605       } break;
8606       case ButtonRelease : {
8607         do {
8608           _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
8609           if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
8610           switch (event.xbutton.button) {
8611           case 1 : set_button(1,false); break;
8612           case 3 : set_button(2,false); break;
8613           case 2 : set_button(3,false); break;
8614           case 4 : set_wheel(1); break;
8615           case 5 : set_wheel(-1); break;
8616           }
8617         } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
8618       } break;
8619       case KeyPress : {
8620         char tmp = 0; KeySym ksym;
8621         XLookupString(&event.xkey,&tmp,1,&ksym,0);
8622         set_key((unsigned int)ksym,true);
8623       } break;
8624       case KeyRelease : {
8625         char keys_return[32];  // Check that the key has been physically unpressed.
8626         XQueryKeymap(dpy,keys_return);
8627         const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
8628         const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
8629         if (!is_key_pressed) {
8630           char tmp = 0; KeySym ksym;
8631           XLookupString(&event.xkey,&tmp,1,&ksym,0);
8632           set_key((unsigned int)ksym,false);
8633         }
8634       } break;
8635       case EnterNotify: {
8636         while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
8637         _mouse_x = event.xmotion.x;
8638         _mouse_y = event.xmotion.y;
8639         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
8640       } break;
8641       case LeaveNotify : {
8642         while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
8643         _mouse_x = _mouse_y = -1; _is_event = true;
8644         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8645       } break;
8646       case MotionNotify : {
8647         while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
8648         _mouse_x = event.xmotion.x;
8649         _mouse_y = event.xmotion.y;
8650         if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
8651         _is_event = true;
8652         pthread_cond_broadcast(&cimg::X11_attr().wait_event);
8653       } break;
8654       }
8655     }
8656 
8657     static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows.
8658       Display *const dpy = cimg::X11_attr().display;
8659       XEvent event;
8660       pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
8661       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
8662       if (!arg) for ( ; ; ) {
8663         cimg_lock_display();
8664         bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
8665         if (!event_flag) event_flag = XCheckMaskEvent(dpy,
8666                                                       ExposureMask | StructureNotifyMask | ButtonPressMask |
8667                                                       KeyPressMask | PointerMotionMask | EnterWindowMask |
8668                                                       LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event);
8669         if (event_flag)
8670           for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
8671             if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
8672               cimg::X11_attr().wins[i]->_handle_events(&event);
8673         cimg_unlock_display();
8674         pthread_testcancel();
8675         cimg::sleep(8);
8676       }
8677       return 0;
8678     }
8679 
8680     void _set_colormap(Colormap& _colormap, const unsigned int dim) {
8681       XColor *const colormap = new XColor[256];
8682       switch (dim) {
8683       case 1 : { // colormap for greyscale images
8684         for (unsigned int index = 0; index<256; ++index) {
8685           colormap[index].pixel = index;
8686           colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
8687           colormap[index].flags = DoRed | DoGreen | DoBlue;
8688         }
8689       } break;
8690       case 2 : { // colormap for RG images
8691         for (unsigned int index = 0, r = 8; r<256; r+=16)
8692           for (unsigned int g = 8; g<256; g+=16) {
8693             colormap[index].pixel = index;
8694             colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
8695             colormap[index].green = (unsigned short)(g<<8);
8696             colormap[index++].flags = DoRed | DoGreen | DoBlue;
8697           }
8698       } break;
8699       default : { // colormap for RGB images
8700         for (unsigned int index = 0, r = 16; r<256; r+=32)
8701           for (unsigned int g = 16; g<256; g+=32)
8702             for (unsigned int b = 32; b<256; b+=64) {
8703               colormap[index].pixel = index;
8704               colormap[index].red = (unsigned short)(r<<8);
8705               colormap[index].green = (unsigned short)(g<<8);
8706               colormap[index].blue = (unsigned short)(b<<8);
8707               colormap[index++].flags = DoRed | DoGreen | DoBlue;
8708             }
8709       }
8710       }
8711       XStoreColors(cimg::X11_attr().display,_colormap,colormap,256);
8712       delete[] colormap;
8713     }
8714 
8715     void _map_window() {
8716       Display *const dpy = cimg::X11_attr().display;
8717       bool is_exposed = false, is_mapped = false;
8718       XWindowAttributes attr;
8719       XEvent event;
8720       XMapRaised(dpy,_window);
8721       do { // Wait for the window to be mapped.
8722         XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
8723         switch (event.type) {
8724         case MapNotify : is_mapped = true; break;
8725         case Expose : is_exposed = true; break;
8726         }
8727       } while (!is_exposed || !is_mapped);
8728       do { // Wait for the window to be visible.
8729         XGetWindowAttributes(dpy,_window,&attr);
8730         if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
8731       } while (attr.map_state!=IsViewable);
8732       _window_x = attr.x;
8733       _window_y = attr.y;
8734     }
8735 
8736     void _paint(const bool wait_expose=true) {
8737       if (_is_closed || !_image) return;
8738       Display *const dpy = cimg::X11_attr().display;
8739       if (wait_expose) { // Send an expose event sticked to display window to force repaint.
8740         XEvent event;
8741         event.xexpose.type = Expose;
8742         event.xexpose.serial = 0;
8743         event.xexpose.send_event = 1;
8744         event.xexpose.display = dpy;
8745         event.xexpose.window = _window;
8746         event.xexpose.x = 0;
8747         event.xexpose.y = 0;
8748         event.xexpose.width = width();
8749         event.xexpose.height = height();
8750         event.xexpose.count = 0;
8751         XSendEvent(dpy,_window,0,0,&event);
8752       } else { // Repaint directly (may be called from the expose event).
8753         GC gc = DefaultGC(dpy,DefaultScreen(dpy));
8754 #ifdef cimg_use_xshm
8755         if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
8756         else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
8757 #else
8758         XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
8759 #endif
8760       }
8761     }
8762 
8763     template<typename T>
8764     void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
8765       Display *const dpy = cimg::X11_attr().display;
8766       cimg::unused(pixel_type);
8767 
8768 #ifdef cimg_use_xshm
8769       if (_shminfo) {
8770         XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
8771         XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
8772                                                cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
8773         if (!nimage) { delete nshminfo; return; }
8774         else {
8775           nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
8776           if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
8777           else {
8778             nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
8779             if (nshminfo->shmaddr==(char*)-1) {
8780               shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return;
8781             } else {
8782               nshminfo->readOnly = 0;
8783               cimg::X11_attr().is_shm_enabled = true;
8784               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
8785               XShmAttach(dpy,nshminfo);
8786               XFlush(dpy);
8787               XSetErrorHandler(oldXErrorHandler);
8788               if (!cimg::X11_attr().is_shm_enabled) {
8789                 shmdt(nshminfo->shmaddr);
8790                 shmctl(nshminfo->shmid,IPC_RMID,0);
8791                 XDestroyImage(nimage);
8792                 delete nshminfo;
8793                 return;
8794               } else {
8795                 T *const ndata = (T*)nimage->data;
8796                 if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
8797                 else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
8798                 XShmDetach(dpy,_shminfo);
8799                 XDestroyImage(_image);
8800                 shmdt(_shminfo->shmaddr);
8801                 shmctl(_shminfo->shmid,IPC_RMID,0);
8802                 delete _shminfo;
8803                 _shminfo = nshminfo;
8804                 _image = nimage;
8805                 _data = (void*)ndata;
8806               }
8807             }
8808           }
8809         }
8810       } else
8811 #endif
8812         {
8813           T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
8814           if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
8815           else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
8816           _data = (void*)ndata;
8817           XDestroyImage(_image);
8818           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
8819                                 cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
8820         }
8821     }
8822 
8823     void _init_fullscreen() {
8824       if (!_is_fullscreen || _is_closed) return;
8825       Display *const dpy = cimg::X11_attr().display;
8826       _background_window = 0;
8827 
8828 #ifdef cimg_use_xrandr
8829       int foo;
8830       if (XRRQueryExtension(dpy,&foo,&foo)) {
8831         XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
8832         if (!cimg::X11_attr().resolutions) {
8833           cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
8834           cimg::X11_attr().nb_resolutions = (unsigned int)foo;
8835         }
8836         if (cimg::X11_attr().resolutions) {
8837           cimg::X11_attr().curr_resolution = 0;
8838           for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
8839             const unsigned int
8840               nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
8841               nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
8842             if (nw>=_width && nh>=_height &&
8843                 nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
8844                 nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
8845               cimg::X11_attr().curr_resolution = i;
8846           }
8847           if (cimg::X11_attr().curr_resolution>0) {
8848             XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
8849             XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
8850                                cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
8851             XRRFreeScreenConfigInfo(config);
8852             XSync(dpy,0);
8853           }
8854         }
8855       }
8856       if (!cimg::X11_attr().resolutions)
8857         cimg::warn(_cimgdisplay_instance
8858                    "init_fullscreen(): Xrandr extension not supported by the X server.",
8859                    cimgdisplay_instance);
8860 #endif
8861 
8862       const unsigned int sx = screen_width(), sy = screen_height();
8863       if (sx==_width && sy==_height) return;
8864       XSetWindowAttributes winattr;
8865       winattr.override_redirect = 1;
8866       _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
8867                                          InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
8868       const cimg_ulong buf_size = (cimg_ulong)sx*sy*(cimg::X11_attr().nb_bits==8?1:
8869                                                      (cimg::X11_attr().nb_bits==16?2:4));
8870       void *background_data = std::malloc(buf_size);
8871       std::memset(background_data,0,buf_size);
8872       XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
8873                                               ZPixmap,0,(char*)background_data,sx,sy,8,0);
8874       XEvent event;
8875       XSelectInput(dpy,_background_window,StructureNotifyMask);
8876       XMapRaised(dpy,_background_window);
8877       do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
8878       while (event.type!=MapNotify);
8879       GC gc = DefaultGC(dpy,DefaultScreen(dpy));
8880 #ifdef cimg_use_xshm
8881       if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,0);
8882       else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
8883 #else
8884       XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
8885 #endif
8886       XWindowAttributes attr;
8887       XGetWindowAttributes(dpy,_background_window,&attr);
8888       while (attr.map_state!=IsViewable) XSync(dpy,0);
8889       XDestroyImage(background_image);
8890     }
8891 
8892     void _desinit_fullscreen() {
8893       if (!_is_fullscreen) return;
8894       Display *const dpy = cimg::X11_attr().display;
8895       XUngrabKeyboard(dpy,CurrentTime);
8896 #ifdef cimg_use_xrandr
8897       if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
8898         XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
8899         XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
8900         XRRFreeScreenConfigInfo(config);
8901         XSync(dpy,0);
8902         cimg::X11_attr().curr_resolution = 0;
8903       }
8904 #endif
8905       if (_background_window) XDestroyWindow(dpy,_background_window);
8906       _background_window = 0;
8907       _is_fullscreen = false;
8908     }
8909 
8910     static int _assign_xshm(Display *dpy, XErrorEvent *error) {
8911       cimg::unused(dpy,error);
8912       cimg::X11_attr().is_shm_enabled = false;
8913       return 0;
8914     }
8915 
8916     void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
8917                  const unsigned int normalization_type=3,
8918                  const bool fullscreen_flag=false, const bool closed_flag=false) {
8919       cimg::mutex(14);
8920 
8921       // Allocate space for window title
8922       const char *const nptitle = ptitle?ptitle:"";
8923       const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
8924       char *const tmp_title = s?new char[s]:0;
8925       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
8926 
8927       // Destroy previous display window if existing
8928       if (!is_empty()) assign();
8929 
8930       // Open X11 display and retrieve graphical properties.
8931       Display* &dpy = cimg::X11_attr().display;
8932       if (!dpy) {
8933         dpy = XOpenDisplay(0);
8934         if (!dpy)
8935           throw CImgDisplayException(_cimgdisplay_instance
8936                                      "assign(): Failed to open X11 display.",
8937                                      cimgdisplay_instance);
8938 
8939         cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
8940         if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 &&
8941             cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
8942           throw CImgDisplayException(_cimgdisplay_instance
8943                                      "assign(): Invalid %u bits screen mode detected "
8944                                      "(only 8, 16, 24 and 32 bits modes are managed).",
8945                                      cimgdisplay_instance,
8946                                      cimg::X11_attr().nb_bits);
8947         XVisualInfo vtemplate;
8948         vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
8949         int nb_visuals;
8950         XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
8951         if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
8952         cimg::X11_attr().byte_order = ImageByteOrder(dpy);
8953 	XFree(vinfo);
8954 
8955         cimg_lock_display();
8956         cimg::X11_attr().events_thread = new pthread_t;
8957         pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0);
8958       } else cimg_lock_display();
8959 
8960       // Set display variables.
8961       _width = std::min(dimw,(unsigned int)screen_width());
8962       _height = std::min(dimh,(unsigned int)screen_height());
8963       _normalization = normalization_type<4?normalization_type:3;
8964       _is_fullscreen = fullscreen_flag;
8965       _window_x = _window_y = 0;
8966       _is_closed = closed_flag;
8967       _title = tmp_title;
8968       flush();
8969 
8970       // Create X11 window (and LUT, if 8bits display)
8971       if (_is_fullscreen) {
8972         if (!_is_closed) _init_fullscreen();
8973         const unsigned int sx = screen_width(), sy = screen_height();
8974         XSetWindowAttributes winattr;
8975         winattr.override_redirect = 1;
8976         _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0,
8977                                 InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
8978       } else
8979         _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
8980 
8981       XSelectInput(dpy,_window,
8982 		   ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
8983 		   EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
8984 
8985       XStoreName(dpy,_window,_title?_title:" ");
8986       if (cimg::X11_attr().nb_bits==8) {
8987         _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
8988         _set_colormap(_colormap,3);
8989         XSetWindowColormap(dpy,_window,_colormap);
8990       }
8991 
8992       static const char *const _window_class = cimg_appname;
8993       XClassHint *const window_class = XAllocClassHint();
8994       window_class->res_name = (char*)_window_class;
8995       window_class->res_class = (char*)_window_class;
8996       XSetClassHint(dpy,_window,window_class);
8997       XFree(window_class);
8998 
8999       _window_width = _width;
9000       _window_height = _height;
9001 
9002       // Create XImage
9003 #ifdef cimg_use_xshm
9004       _shminfo = 0;
9005       if (XShmQueryExtension(dpy)) {
9006         _shminfo = new XShmSegmentInfo;
9007         _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
9008                                  ZPixmap,0,_shminfo,_width,_height);
9009         if (!_image) { delete _shminfo; _shminfo = 0; }
9010         else {
9011           _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
9012           if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
9013           else {
9014             _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
9015             if (_shminfo->shmaddr==(char*)-1) {
9016               shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
9017             } else {
9018               _shminfo->readOnly = 0;
9019               cimg::X11_attr().is_shm_enabled = true;
9020               XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
9021               XShmAttach(dpy,_shminfo);
9022               XSync(dpy,0);
9023               XSetErrorHandler(oldXErrorHandler);
9024               if (!cimg::X11_attr().is_shm_enabled) {
9025                 shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image);
9026                 delete _shminfo; _shminfo = 0;
9027               }
9028             }
9029           }
9030         }
9031       }
9032       if (!_shminfo)
9033 #endif
9034         {
9035           const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1:
9036                                                                   (cimg::X11_attr().nb_bits==16?2:4));
9037           _data = std::malloc(buf_size);
9038           _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
9039                                 ZPixmap,0,(char*)_data,_width,_height,8,0);
9040         }
9041 
9042       _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
9043       _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
9044       XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
9045 
9046       if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
9047       cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
9048       if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); }
9049       cimg_unlock_display();
9050       cimg::mutex(14,0);
9051     }
9052 
9053     CImgDisplay& assign() {
9054       if (is_empty()) return flush();
9055       Display *const dpy = cimg::X11_attr().display;
9056       cimg_lock_display();
9057 
9058       // Remove display window from event thread list.
9059       unsigned int i;
9060       for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
9061       for ( ; i<cimg::X11_attr().nb_wins - 1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i + 1];
9062       --cimg::X11_attr().nb_wins;
9063 
9064       // Destroy window, image, colormap and title.
9065       if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
9066       XDestroyWindow(dpy,_window);
9067       _window = 0;
9068 #ifdef cimg_use_xshm
9069       if (_shminfo) {
9070         XShmDetach(dpy,_shminfo);
9071         XDestroyImage(_image);
9072         shmdt(_shminfo->shmaddr);
9073         shmctl(_shminfo->shmid,IPC_RMID,0);
9074         delete _shminfo;
9075         _shminfo = 0;
9076       } else
9077 #endif
9078         XDestroyImage(_image);
9079       _data = 0; _image = 0;
9080       if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
9081       _colormap = 0;
9082       XSync(dpy,0);
9083 
9084       // Reset display variables.
9085       delete[] _title;
9086       _width = _height = _normalization = _window_width = _window_height = 0;
9087       _window_x = _window_y = 0;
9088       _is_fullscreen = false;
9089       _is_closed = true;
9090       _min = _max = 0;
9091       _title = 0;
9092       flush();
9093 
9094       cimg_unlock_display();
9095       return *this;
9096     }
9097 
9098     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
9099                         const unsigned int normalization_type=3,
9100                         const bool fullscreen_flag=false, const bool closed_flag=false) {
9101       if (!dimw || !dimh) return assign();
9102       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
9103       _min = _max = 0;
9104       std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
9105                            (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*
9106                   (size_t)_width*_height);
9107       return paint();
9108     }
9109 
9110     template<typename T>
9111     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
9112                         const unsigned int normalization_type=3,
9113                         const bool fullscreen_flag=false, const bool closed_flag=false) {
9114       if (!img) return assign();
9115       CImg<T> tmp;
9116       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
9117                                                                            (img._height - 1)/2,
9118                                                                            (img._depth - 1)/2));
9119       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
9120       if (_normalization==2) _min = (float)nimg.min_max(_max);
9121       return render(nimg).paint();
9122     }
9123 
9124     template<typename T>
9125     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
9126                         const unsigned int normalization_type=3,
9127                         const bool fullscreen_flag=false, const bool closed_flag=false) {
9128       if (!list) return assign();
9129       CImg<T> tmp;
9130       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
9131                                                                                            (img._height - 1)/2,
9132                                                                                            (img._depth - 1)/2));
9133       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
9134       if (_normalization==2) _min = (float)nimg.min_max(_max);
9135       return render(nimg).paint();
9136     }
9137 
9138     CImgDisplay& assign(const CImgDisplay& disp) {
9139       if (!disp) return assign();
9140       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
9141       std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
9142                                     cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
9143                                     sizeof(unsigned int))*(size_t)_width*_height);
9144       return paint();
9145     }
9146 
9147     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
9148       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
9149       if (is_empty()) return assign(nwidth,nheight);
9150       Display *const dpy = cimg::X11_attr().display;
9151       const unsigned int
9152         tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
9153         tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
9154         dimx = tmpdimx?tmpdimx:1,
9155         dimy = tmpdimy?tmpdimy:1;
9156       if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
9157         show();
9158         cimg_lock_display();
9159         if (_window_width!=dimx || _window_height!=dimy) {
9160           XWindowAttributes attr;
9161           for (unsigned int i = 0; i<10; ++i) {
9162             XResizeWindow(dpy,_window,dimx,dimy);
9163             XGetWindowAttributes(dpy,_window,&attr);
9164             if (attr.width==(int)dimx && attr.height==(int)dimy) break;
9165             cimg::wait(5);
9166           }
9167         }
9168         if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
9169           case 8 :  { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
9170           case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
9171           default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
9172           }
9173         _window_width = _width = dimx; _window_height = _height = dimy;
9174         cimg_unlock_display();
9175       }
9176       _is_resized = false;
9177       if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2);
9178       if (force_redraw) return paint();
9179       return *this;
9180     }
9181 
9182     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
9183       if (is_empty()) return *this;
9184       if (force_redraw) {
9185         const cimg_ulong buf_size = (cimg_ulong)_width*_height*
9186           (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
9187         void *image_data = std::malloc(buf_size);
9188         std::memcpy(image_data,_data,buf_size);
9189         assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
9190         std::memcpy(_data,image_data,buf_size);
9191         std::free(image_data);
9192         return paint();
9193       }
9194       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
9195     }
9196 
9197     CImgDisplay& show() {
9198       if (is_empty() || !_is_closed) return *this;
9199       cimg_lock_display();
9200       if (_is_fullscreen) _init_fullscreen();
9201       _map_window();
9202       _is_closed = false;
9203       cimg_unlock_display();
9204       return paint();
9205     }
9206 
9207     CImgDisplay& close() {
9208       if (is_empty() || _is_closed) return *this;
9209       Display *const dpy = cimg::X11_attr().display;
9210       cimg_lock_display();
9211       if (_is_fullscreen) _desinit_fullscreen();
9212       XUnmapWindow(dpy,_window);
9213       _window_x = _window_y = -1;
9214       _is_closed = true;
9215       cimg_unlock_display();
9216       return *this;
9217     }
9218 
9219     CImgDisplay& move(const int posx, const int posy) {
9220       if (is_empty()) return *this;
9221       if (_window_x!=posx || _window_y!=posy) {
9222         show();
9223         Display *const dpy = cimg::X11_attr().display;
9224         cimg_lock_display();
9225         XMoveWindow(dpy,_window,posx,posy);
9226         _window_x = posx; _window_y = posy;
9227         cimg_unlock_display();
9228       }
9229       _is_moved = false;
9230       return paint();
9231     }
9232 
9233     CImgDisplay& show_mouse() {
9234       if (is_empty()) return *this;
9235       Display *const dpy = cimg::X11_attr().display;
9236       cimg_lock_display();
9237       XUndefineCursor(dpy,_window);
9238       cimg_unlock_display();
9239       return *this;
9240     }
9241 
9242     CImgDisplay& hide_mouse() {
9243       if (is_empty()) return *this;
9244       Display *const dpy = cimg::X11_attr().display;
9245       cimg_lock_display();
9246       static const char pix_data[8] = { 0 };
9247       XColor col;
9248       col.red = col.green = col.blue = 0;
9249       Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
9250       Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
9251       XFreePixmap(dpy,pix);
9252       XDefineCursor(dpy,_window,cur);
9253       cimg_unlock_display();
9254       return *this;
9255     }
9256 
9257     CImgDisplay& set_mouse(const int posx, const int posy) {
9258       if (is_empty() || _is_closed) return *this;
9259       Display *const dpy = cimg::X11_attr().display;
9260       cimg_lock_display();
9261       XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
9262       _mouse_x = posx; _mouse_y = posy;
9263       _is_moved = false;
9264       XSync(dpy,0);
9265       cimg_unlock_display();
9266       return *this;
9267     }
9268 
9269     CImgDisplay& set_title(const char *const format, ...) {
9270       if (is_empty()) return *this;
9271       char *const tmp = new char[1024];
9272       va_list ap;
9273       va_start(ap, format);
9274       cimg_vsnprintf(tmp,1024,format,ap);
9275       va_end(ap);
9276       if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
9277       delete[] _title;
9278       const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
9279       _title = new char[s];
9280       std::memcpy(_title,tmp,s*sizeof(char));
9281       Display *const dpy = cimg::X11_attr().display;
9282       cimg_lock_display();
9283       XStoreName(dpy,_window,tmp);
9284       cimg_unlock_display();
9285       delete[] tmp;
9286       return *this;
9287     }
9288 
9289     template<typename T>
9290     CImgDisplay& display(const CImg<T>& img) {
9291       if (!img)
9292         throw CImgArgumentException(_cimgdisplay_instance
9293                                     "display(): Empty specified image.",
9294                                     cimgdisplay_instance);
9295       if (is_empty()) return assign(img);
9296       return render(img).paint(false);
9297     }
9298 
9299     CImgDisplay& paint(const bool wait_expose=true) {
9300       if (is_empty()) return *this;
9301       cimg_lock_display();
9302       _paint(wait_expose);
9303       cimg_unlock_display();
9304       return *this;
9305     }
9306 
9307     template<typename T>
9308     CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
9309       if (!img)
9310         throw CImgArgumentException(_cimgdisplay_instance
9311                                     "render(): Empty specified image.",
9312                                     cimgdisplay_instance);
9313       if (is_empty()) return *this;
9314       if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
9315                                                              (img._depth - 1)/2));
9316       if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height))
9317         return render(img.get_resize(_width,_height,1,-100,1));
9318       if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
9319         static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
9320         return render(img.get_index(default_colormap,1,false));
9321       }
9322 
9323       const T
9324         *data1 = img._data,
9325         *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
9326         *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
9327 
9328       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
9329       cimg_lock_display();
9330 
9331       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
9332         _min = _max = 0;
9333         switch (cimg::X11_attr().nb_bits) {
9334         case 8 : { // 256 colormap, no normalization
9335           _set_colormap(_colormap,img._spectrum);
9336           unsigned char
9337             *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
9338             new unsigned char[(size_t)img._width*img._height],
9339             *ptrd = (unsigned char*)ndata;
9340           switch (img._spectrum) {
9341           case 1 :
9342             for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9343               (*ptrd++) = (unsigned char)*(data1++);
9344             break;
9345           case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9346 	      const unsigned char
9347                 R = (unsigned char)*(data1++),
9348                 G = (unsigned char)*(data2++);
9349 	      (*ptrd++) = (R&0xf0) | (G>>4);
9350 	    } break;
9351           default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9352 	      const unsigned char
9353                 R = (unsigned char)*(data1++),
9354                 G = (unsigned char)*(data2++),
9355                 B = (unsigned char)*(data3++);
9356 	      (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
9357 	    }
9358           }
9359           if (ndata!=_data) {
9360             _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
9361             delete[] ndata;
9362           }
9363         } break;
9364         case 16 : { // 16 bits colors, no normalization
9365           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
9366             new unsigned short[(size_t)img._width*img._height];
9367           unsigned char *ptrd = (unsigned char*)ndata;
9368           const unsigned int M = 248;
9369           switch (img._spectrum) {
9370           case 1 :
9371             if (cimg::X11_attr().byte_order)
9372               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9373                 const unsigned char val = (unsigned char)*(data1++), G = val>>2;
9374                 ptrd[0] = (val&M) | (G>>3);
9375                 ptrd[1] = (G<<5) | (G>>1);
9376                 ptrd+=2;
9377               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9378                 const unsigned char val = (unsigned char)*(data1++), G = val>>2;
9379                 ptrd[0] = (G<<5) | (G>>1);
9380                 ptrd[1] = (val&M) | (G>>3);
9381                 ptrd+=2;
9382               }
9383             break;
9384           case 2 :
9385             if (cimg::X11_attr().byte_order)
9386               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9387                 const unsigned char G = (unsigned char)*(data2++)>>2;
9388                 ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
9389                 ptrd[1] = (G<<5);
9390                 ptrd+=2;
9391               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9392                 const unsigned char G = (unsigned char)*(data2++)>>2;
9393                 ptrd[0] = (G<<5);
9394                 ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
9395                 ptrd+=2;
9396               }
9397             break;
9398           default :
9399             if (cimg::X11_attr().byte_order)
9400               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9401                 const unsigned char G = (unsigned char)*(data2++)>>2;
9402                 ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
9403                 ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3);
9404                 ptrd+=2;
9405               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9406                 const unsigned char G = (unsigned char)*(data2++)>>2;
9407                 ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3);
9408                 ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
9409                 ptrd+=2;
9410               }
9411           }
9412           if (ndata!=_data) {
9413             _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
9414             delete[] ndata;
9415           }
9416         } break;
9417         default : { // 24 bits colors, no normalization
9418           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
9419             new unsigned int[(size_t)img._width*img._height];
9420           if (sizeof(int)==4) { // 32 bits int uses optimized version
9421             unsigned int *ptrd = ndata;
9422             switch (img._spectrum) {
9423             case 1 :
9424               if (cimg::X11_attr().byte_order==cimg::endianness())
9425                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9426                   const unsigned char val = (unsigned char)*(data1++);
9427                   *(ptrd++) = (val<<16) | (val<<8) | val;
9428                 }
9429               else
9430                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9431                  const unsigned char val = (unsigned char)*(data1++);
9432                   *(ptrd++) = (val<<16) | (val<<8) | val;
9433                 }
9434               break;
9435             case 2 :
9436               if (cimg::X11_attr().byte_order==cimg::endianness())
9437                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9438                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
9439               else
9440                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9441                   *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
9442               break;
9443             default :
9444               if (cimg::X11_attr().byte_order==cimg::endianness())
9445                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9446                   *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) |
9447                     (unsigned char)*(data3++);
9448               else
9449                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9450                   *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) |
9451                     ((unsigned char)*(data1++)<<8);
9452             }
9453           } else {
9454             unsigned char *ptrd = (unsigned char*)ndata;
9455             switch (img._spectrum) {
9456             case 1 :
9457               if (cimg::X11_attr().byte_order)
9458                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9459                   ptrd[0] = 0;
9460                   ptrd[1] = (unsigned char)*(data1++);
9461                   ptrd[2] = 0;
9462                   ptrd[3] = 0;
9463                   ptrd+=4;
9464                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9465                   ptrd[0] = 0;
9466                   ptrd[1] = 0;
9467                   ptrd[2] = (unsigned char)*(data1++);
9468                   ptrd[3] = 0;
9469                   ptrd+=4;
9470                 }
9471               break;
9472             case 2 :
9473               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
9474               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9475                 ptrd[0] = 0;
9476                 ptrd[1] = (unsigned char)*(data2++);
9477                 ptrd[2] = (unsigned char)*(data1++);
9478                 ptrd[3] = 0;
9479                 ptrd+=4;
9480               }
9481               break;
9482             default :
9483               if (cimg::X11_attr().byte_order)
9484                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9485                   ptrd[0] = 0;
9486                   ptrd[1] = (unsigned char)*(data1++);
9487                   ptrd[2] = (unsigned char)*(data2++);
9488                   ptrd[3] = (unsigned char)*(data3++);
9489                   ptrd+=4;
9490                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9491                   ptrd[0] = (unsigned char)*(data3++);
9492                   ptrd[1] = (unsigned char)*(data2++);
9493                   ptrd[2] = (unsigned char)*(data1++);
9494                   ptrd[3] = 0;
9495                   ptrd+=4;
9496                 }
9497             }
9498           }
9499           if (ndata!=_data) {
9500             _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
9501             delete[] ndata;
9502           }
9503         }
9504         }
9505       } else {
9506         if (_normalization==3) {
9507           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
9508           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
9509         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
9510         const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
9511         switch (cimg::X11_attr().nb_bits) {
9512         case 8 : { // 256 colormap, with normalization
9513           _set_colormap(_colormap,img._spectrum);
9514           unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
9515             new unsigned char[(size_t)img._width*img._height];
9516           unsigned char *ptrd = (unsigned char*)ndata;
9517           switch (img._spectrum) {
9518           case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9519               const unsigned char R = (unsigned char)((*(data1++) - _min)*mm);
9520               *(ptrd++) = R;
9521             } break;
9522           case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9523               const unsigned char
9524                 R = (unsigned char)((*(data1++) - _min)*mm),
9525                 G = (unsigned char)((*(data2++) - _min)*mm);
9526             (*ptrd++) = (R&0xf0) | (G>>4);
9527           } break;
9528           default :
9529             for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9530               const unsigned char
9531                 R = (unsigned char)((*(data1++) - _min)*mm),
9532                 G = (unsigned char)((*(data2++) - _min)*mm),
9533                 B = (unsigned char)((*(data3++) - _min)*mm);
9534               *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
9535             }
9536           }
9537           if (ndata!=_data) {
9538             _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
9539             delete[] ndata;
9540           }
9541         } break;
9542         case 16 : { // 16 bits colors, with normalization
9543           unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
9544             new unsigned short[(size_t)img._width*img._height];
9545           unsigned char *ptrd = (unsigned char*)ndata;
9546           const unsigned int M = 248;
9547           switch (img._spectrum) {
9548           case 1 :
9549             if (cimg::X11_attr().byte_order)
9550               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9551                 const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
9552                 ptrd[0] = (val&M) | (G>>3);
9553                 ptrd[1] = (G<<5) | (val>>3);
9554                 ptrd+=2;
9555               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9556                 const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
9557                 ptrd[0] = (G<<5) | (val>>3);
9558                 ptrd[1] = (val&M) | (G>>3);
9559                 ptrd+=2;
9560               }
9561             break;
9562           case 2 :
9563             if (cimg::X11_attr().byte_order)
9564               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9565                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
9566                 ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
9567                 ptrd[1] = (G<<5);
9568                 ptrd+=2;
9569               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9570                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
9571                 ptrd[0] = (G<<5);
9572                 ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
9573                 ptrd+=2;
9574               }
9575             break;
9576           default :
9577             if (cimg::X11_attr().byte_order)
9578               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9579                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
9580                 ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
9581                 ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
9582                 ptrd+=2;
9583               } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9584                 const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
9585                 ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
9586                 ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
9587                 ptrd+=2;
9588               }
9589           }
9590           if (ndata!=_data) {
9591             _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
9592             delete[] ndata;
9593           }
9594         } break;
9595         default : { // 24 bits colors, with normalization
9596           unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
9597             new unsigned int[(size_t)img._width*img._height];
9598           if (sizeof(int)==4) { // 32 bits int uses optimized version
9599             unsigned int *ptrd = ndata;
9600             switch (img._spectrum) {
9601             case 1 :
9602               if (cimg::X11_attr().byte_order==cimg::endianness())
9603                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9604                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
9605                   *(ptrd++) = (val<<16) | (val<<8) | val;
9606                 }
9607               else
9608                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9609                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
9610                   *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
9611                 }
9612               break;
9613             case 2 :
9614               if (cimg::X11_attr().byte_order==cimg::endianness())
9615                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9616                   *(ptrd++) =
9617                     ((unsigned char)((*(data1++) - _min)*mm)<<16) |
9618                     ((unsigned char)((*(data2++) - _min)*mm)<<8);
9619               else
9620                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9621                   *(ptrd++) =
9622                     ((unsigned char)((*(data2++) - _min)*mm)<<16) |
9623                     ((unsigned char)((*(data1++) - _min)*mm)<<8);
9624               break;
9625             default :
9626               if (cimg::X11_attr().byte_order==cimg::endianness())
9627                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9628                   *(ptrd++) =
9629                     ((unsigned char)((*(data1++) - _min)*mm)<<16) |
9630                     ((unsigned char)((*(data2++) - _min)*mm)<<8) |
9631                     (unsigned char)((*(data3++) - _min)*mm);
9632               else
9633                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
9634                   *(ptrd++) =
9635                     ((unsigned char)((*(data3++) - _min)*mm)<<24) |
9636                     ((unsigned char)((*(data2++) - _min)*mm)<<16) |
9637                     ((unsigned char)((*(data1++) - _min)*mm)<<8);
9638             }
9639           } else {
9640             unsigned char *ptrd = (unsigned char*)ndata;
9641             switch (img._spectrum) {
9642             case 1 :
9643               if (cimg::X11_attr().byte_order)
9644                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9645                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
9646                   ptrd[0] = 0;
9647                   ptrd[1] = val;
9648                   ptrd[2] = val;
9649                   ptrd[3] = val;
9650                   ptrd+=4;
9651                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9652                   const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
9653                   ptrd[0] = val;
9654                   ptrd[1] = val;
9655                   ptrd[2] = val;
9656                   ptrd[3] = 0;
9657                   ptrd+=4;
9658                 }
9659               break;
9660             case 2 :
9661               if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
9662               for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9663                 ptrd[0] = 0;
9664                 ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
9665                 ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
9666                 ptrd[3] = 0;
9667                 ptrd+=4;
9668               }
9669               break;
9670             default :
9671               if (cimg::X11_attr().byte_order)
9672                 for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9673                   ptrd[0] = 0;
9674                   ptrd[1] = (unsigned char)((*(data1++) - _min)*mm);
9675                   ptrd[2] = (unsigned char)((*(data2++) - _min)*mm);
9676                   ptrd[3] = (unsigned char)((*(data3++) - _min)*mm);
9677                   ptrd+=4;
9678                 } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9679                   ptrd[0] = (unsigned char)((*(data3++) - _min)*mm);
9680                   ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
9681                   ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
9682                   ptrd[3] = 0;
9683                   ptrd+=4;
9684                 }
9685             }
9686           }
9687           if (ndata!=_data) {
9688             _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
9689             delete[] ndata;
9690           }
9691 	}
9692         }
9693       }
9694       cimg_unlock_display();
9695       return *this;
9696     }
9697 
9698     template<typename T>
9699     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
9700       img.assign();
9701       Display *dpy = cimg::X11_attr().display;
9702       cimg_lock_display();
9703       if (!dpy) {
9704         dpy = XOpenDisplay(0);
9705         if (!dpy)
9706           throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display.");
9707       }
9708       Window root = DefaultRootWindow(dpy);
9709       XWindowAttributes gwa;
9710       XGetWindowAttributes(dpy,root,&gwa);
9711       const int width = gwa.width, height = gwa.height;
9712       int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
9713       if (_x0>_x1) cimg::swap(_x0,_x1);
9714       if (_y0>_y1) cimg::swap(_y0,_y1);
9715 
9716       XImage *image = 0;
9717       if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
9718         _x0 = std::max(_x0,0);
9719         _y0 = std::max(_y0,0);
9720         _x1 = std::min(_x1,width - 1);
9721         _y1 = std::min(_y1,height - 1);
9722         image = XGetImage(dpy,root,_x0,_y0,_x1 - _x0 + 1,_y1 - _y0 + 1,AllPlanes,ZPixmap);
9723 
9724         if (image) {
9725           const unsigned long
9726             red_mask = image->red_mask,
9727             green_mask = image->green_mask,
9728             blue_mask = image->blue_mask;
9729           img.assign(image->width,image->height,1,3);
9730           T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
9731           cimg_forXY(img,x,y) {
9732             const unsigned long pixel = XGetPixel(image,x,y);
9733             *(pR++) = (T)((pixel & red_mask)>>16);
9734             *(pG++) = (T)((pixel & green_mask)>>8);
9735             *(pB++) = (T)(pixel & blue_mask);
9736           }
9737           XDestroyImage(image);
9738         }
9739       }
9740       if (!cimg::X11_attr().display) XCloseDisplay(dpy);
9741       cimg_unlock_display();
9742       if (img.is_empty())
9743         throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
9744                                    "with coordinates (%d,%d)-(%d,%d).",
9745                                    x0,y0,x1,y1);
9746     }
9747 
9748     template<typename T>
9749     const CImgDisplay& snapshot(CImg<T>& img) const {
9750       if (is_empty()) { img.assign(); return *this; }
9751       const unsigned char *ptrs = (unsigned char*)_data;
9752       img.assign(_width,_height,1,3);
9753       T
9754         *data1 = img.data(0,0,0,0),
9755         *data2 = img.data(0,0,0,1),
9756         *data3 = img.data(0,0,0,2);
9757       if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
9758       switch (cimg::X11_attr().nb_bits) {
9759       case 8 : {
9760         for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9761           const unsigned char val = *(ptrs++);
9762           *(data1++) = (T)(val&0xe0);
9763           *(data2++) = (T)((val&0x1c)<<3);
9764           *(data3++) = (T)(val<<6);
9765         }
9766       } break;
9767       case 16 : {
9768         if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9769           const unsigned char
9770             val0 = ptrs[0],
9771             val1 = ptrs[1];
9772           ptrs+=2;
9773           *(data1++) = (T)(val0&0xf8);
9774           *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
9775           *(data3++) = (T)(val1<<3);
9776           } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9777           const unsigned short
9778             val0 = ptrs[0],
9779             val1 = ptrs[1];
9780           ptrs+=2;
9781           *(data1++) = (T)(val1&0xf8);
9782           *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
9783           *(data3++) = (T)(val0<<3);
9784         }
9785       } break;
9786       default : {
9787         if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9788           ++ptrs;
9789           *(data1++) = (T)ptrs[0];
9790           *(data2++) = (T)ptrs[1];
9791           *(data3++) = (T)ptrs[2];
9792           ptrs+=3;
9793           } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
9794             *(data3++) = (T)ptrs[0];
9795             *(data2++) = (T)ptrs[1];
9796             *(data1++) = (T)ptrs[2];
9797             ptrs+=3;
9798             ++ptrs;
9799           }
9800       }
9801       }
9802       return *this;
9803     }
9804 
9805     // Windows-based implementation.
9806     //-------------------------------
9807 #elif cimg_display==2
9808 
9809     bool _is_mouse_tracked, _is_cursor_visible;
9810     HANDLE _thread, _is_created, _mutex;
9811     HWND _window, _background_window;
9812     CLIENTCREATESTRUCT _ccs;
9813     unsigned int *_data;
9814     DEVMODE _curr_mode;
9815     BITMAPINFO _bmi;
9816     HDC _hdc;
9817 
9818     static int screen_width() {
9819       DEVMODE mode;
9820       mode.dmSize = sizeof(DEVMODE);
9821       mode.dmDriverExtra = 0;
9822       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
9823       return (int)mode.dmPelsWidth;
9824     }
9825 
9826     static int screen_height() {
9827       DEVMODE mode;
9828       mode.dmSize = sizeof(DEVMODE);
9829       mode.dmDriverExtra = 0;
9830       EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
9831       return (int)mode.dmPelsHeight;
9832     }
9833 
9834     static void wait_all() {
9835       WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
9836     }
9837 
9838     static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
9839 #ifdef _WIN64
9840       CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
9841 #else
9842       CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
9843 #endif
9844       MSG st_msg;
9845       switch (msg) {
9846       case WM_CLOSE :
9847         disp->_mouse_x = disp->_mouse_y = -1;
9848         disp->_window_x = disp->_window_y = 0;
9849         disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
9850         ReleaseMutex(disp->_mutex);
9851         ShowWindow(disp->_window,SW_HIDE);
9852         disp->_is_event = true;
9853         SetEvent(cimg::Win32_attr().wait_event);
9854         return 0;
9855       case WM_SIZE : {
9856         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
9857         WaitForSingleObject(disp->_mutex,INFINITE);
9858         const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
9859         if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
9860           disp->_window_width = nw;
9861           disp->_window_height = nh;
9862           disp->_mouse_x = disp->_mouse_y = -1;
9863           disp->_is_resized = disp->_is_event = true;
9864           SetEvent(cimg::Win32_attr().wait_event);
9865         }
9866         ReleaseMutex(disp->_mutex);
9867       } break;
9868       case WM_MOVE : {
9869         while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
9870         WaitForSingleObject(disp->_mutex,INFINITE);
9871         const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
9872         if (nx!=disp->_window_x || ny!=disp->_window_y) {
9873           disp->_window_x = nx;
9874           disp->_window_y = ny;
9875           disp->_is_moved = disp->_is_event = true;
9876           SetEvent(cimg::Win32_attr().wait_event);
9877         }
9878         ReleaseMutex(disp->_mutex);
9879       } break;
9880       case WM_PAINT :
9881         disp->paint();
9882         cimg::mutex(15);
9883         if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0);
9884         cimg::mutex(15,0);
9885         break;
9886       case WM_ERASEBKGND :
9887         //        return 0;
9888         break;
9889       case WM_KEYDOWN :
9890         disp->set_key((unsigned int)wParam);
9891         SetEvent(cimg::Win32_attr().wait_event);
9892         break;
9893       case WM_KEYUP :
9894         disp->set_key((unsigned int)wParam,false);
9895         SetEvent(cimg::Win32_attr().wait_event);
9896         break;
9897       case WM_MOUSEMOVE : {
9898         while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
9899         disp->_mouse_x = LOWORD(lParam);
9900         disp->_mouse_y = HIWORD(lParam);
9901 #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
9902         if (!disp->_is_mouse_tracked) {
9903           TRACKMOUSEEVENT tme;
9904           tme.cbSize = sizeof(TRACKMOUSEEVENT);
9905           tme.dwFlags = TME_LEAVE;
9906           tme.hwndTrack = disp->_window;
9907           if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
9908         }
9909 #endif
9910         if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
9911           disp->_mouse_x = disp->_mouse_y = -1;
9912         disp->_is_event = true;
9913         SetEvent(cimg::Win32_attr().wait_event);
9914         cimg::mutex(15);
9915 	if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE)>=0);
9916         cimg::mutex(15,0);
9917       }	break;
9918       case WM_MOUSELEAVE : {
9919         disp->_mouse_x = disp->_mouse_y = -1;
9920         disp->_is_mouse_tracked = false;
9921         cimg::mutex(15);
9922 	while (ShowCursor(TRUE)<0) {}
9923         cimg::mutex(15,0);
9924       } break;
9925       case WM_LBUTTONDOWN :
9926         disp->set_button(1);
9927         SetEvent(cimg::Win32_attr().wait_event);
9928         break;
9929       case WM_RBUTTONDOWN :
9930         disp->set_button(2);
9931         SetEvent(cimg::Win32_attr().wait_event);
9932         break;
9933       case WM_MBUTTONDOWN :
9934         disp->set_button(3);
9935         SetEvent(cimg::Win32_attr().wait_event);
9936         break;
9937       case WM_LBUTTONUP :
9938         disp->set_button(1,false);
9939         SetEvent(cimg::Win32_attr().wait_event);
9940         break;
9941       case WM_RBUTTONUP :
9942         disp->set_button(2,false);
9943         SetEvent(cimg::Win32_attr().wait_event);
9944         break;
9945       case WM_MBUTTONUP :
9946         disp->set_button(3,false);
9947         SetEvent(cimg::Win32_attr().wait_event);
9948         break;
9949       case 0x020A : // WM_MOUSEWHEEL:
9950         disp->set_wheel((int)((short)HIWORD(wParam))/120);
9951         SetEvent(cimg::Win32_attr().wait_event);
9952       }
9953       return DefWindowProc(window,msg,wParam,lParam);
9954     }
9955 
9956     static DWORD WINAPI _events_thread(void* arg) {
9957       CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
9958       const char *const title = (const char*)(((void**)arg)[1]);
9959       MSG msg;
9960       delete[] (void**)arg;
9961       disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
9962       disp->_bmi.bmiHeader.biWidth = disp->width();
9963       disp->_bmi.bmiHeader.biHeight = -disp->height();
9964       disp->_bmi.bmiHeader.biPlanes = 1;
9965       disp->_bmi.bmiHeader.biBitCount = 32;
9966       disp->_bmi.bmiHeader.biCompression = BI_RGB;
9967       disp->_bmi.bmiHeader.biSizeImage = 0;
9968       disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
9969       disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
9970       disp->_bmi.bmiHeader.biClrUsed = 0;
9971       disp->_bmi.bmiHeader.biClrImportant = 0;
9972       disp->_data = new unsigned int[(size_t)disp->_width*disp->_height];
9973       if (!disp->_is_fullscreen) { // Normal window
9974         RECT rect;
9975         rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1;
9976         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
9977         const int
9978           border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2),
9979           border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1);
9980         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
9981                                      WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT,
9982                                      disp->_width + 2*border1, disp->_height + border1 + border2,
9983                                      0,0,0,&(disp->_ccs));
9984         if (!disp->_is_closed) {
9985           GetWindowRect(disp->_window,&rect);
9986           disp->_window_x = rect.left + border1;
9987           disp->_window_y = rect.top + border2;
9988         } else disp->_window_x = disp->_window_y = 0;
9989       } else { // Fullscreen window
9990         const unsigned int
9991           sx = (unsigned int)screen_width(),
9992           sy = (unsigned int)screen_height();
9993         disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
9994                                      WS_POPUP | (disp->_is_closed?0:WS_VISIBLE),
9995                                       (sx - disp->_width)/2,
9996                                       (sy - disp->_height)/2,
9997                                      disp->_width,disp->_height,0,0,0,&(disp->_ccs));
9998         disp->_window_x = disp->_window_y = 0;
9999       }
10000       SetForegroundWindow(disp->_window);
10001       disp->_hdc = GetDC(disp->_window);
10002       disp->_window_width = disp->_width;
10003       disp->_window_height = disp->_height;
10004       disp->flush();
10005 #ifdef _WIN64
10006       SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
10007       SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
10008 #else
10009       SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
10010       SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
10011 #endif
10012       SetEvent(disp->_is_created);
10013       while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
10014       return 0;
10015     }
10016 
10017     CImgDisplay& _update_window_pos() {
10018       if (_is_closed) _window_x = _window_y = -1;
10019       else {
10020         RECT rect;
10021         rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1;
10022         AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
10023         const int
10024           border1 = (int)((rect.right - rect.left + 1 - _width)/2),
10025           border2 = (int)(rect.bottom - rect.top + 1 - _height - border1);
10026         GetWindowRect(_window,&rect);
10027         _window_x = rect.left + border1;
10028         _window_y = rect.top + border2;
10029       }
10030       return *this;
10031     }
10032 
10033     void _init_fullscreen() {
10034       _background_window = 0;
10035       if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
10036       else {
10037         DEVMODE mode;
10038         unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
10039         for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
10040           const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
10041           if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
10042             bestbpp = mode.dmBitsPerPel;
10043             ibest = imode;
10044             bw = nw; bh = nh;
10045           }
10046         }
10047         if (bestbpp) {
10048           _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
10049           EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
10050           EnumDisplaySettings(0,ibest,&mode);
10051           ChangeDisplaySettings(&mode,0);
10052         } else _curr_mode.dmSize = 0;
10053 
10054         const unsigned int
10055           sx = (unsigned int)screen_width(),
10056           sy = (unsigned int)screen_height();
10057         if (sx!=_width || sy!=_height) {
10058           CLIENTCREATESTRUCT background_ccs;
10059           _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs);
10060           SetForegroundWindow(_background_window);
10061         }
10062       }
10063     }
10064 
10065     void _desinit_fullscreen() {
10066       if (!_is_fullscreen) return;
10067       if (_background_window) DestroyWindow(_background_window);
10068       _background_window = 0;
10069       if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
10070       _is_fullscreen = false;
10071     }
10072 
10073     CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
10074                          const unsigned int normalization_type=3,
10075                          const bool fullscreen_flag=false, const bool closed_flag=false) {
10076 
10077       // Allocate space for window title
10078       const char *const nptitle = ptitle?ptitle:"";
10079       const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
10080       char *const tmp_title = s?new char[s]:0;
10081       if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
10082 
10083       // Destroy previous window if existing
10084       if (!is_empty()) assign();
10085 
10086       // Set display variables
10087       _width = std::min(dimw,(unsigned int)screen_width());
10088       _height = std::min(dimh,(unsigned int)screen_height());
10089       _normalization = normalization_type<4?normalization_type:3;
10090       _is_fullscreen = fullscreen_flag;
10091       _window_x = _window_y = 0;
10092       _is_closed = closed_flag;
10093       _is_cursor_visible = true;
10094       _is_mouse_tracked = false;
10095       _title = tmp_title;
10096       flush();
10097       if (_is_fullscreen) _init_fullscreen();
10098 
10099       // Create event thread
10100       void *const arg = (void*)(new void*[2]);
10101       ((void**)arg)[0] = (void*)this;
10102       ((void**)arg)[1] = (void*)_title;
10103       _mutex = CreateMutex(0,FALSE,0);
10104       _is_created = CreateEvent(0,FALSE,FALSE,0);
10105       _thread = CreateThread(0,0,_events_thread,arg,0,0);
10106       WaitForSingleObject(_is_created,INFINITE);
10107       return *this;
10108     }
10109 
10110     CImgDisplay& assign() {
10111       if (is_empty()) return flush();
10112       DestroyWindow(_window);
10113       TerminateThread(_thread,0);
10114       delete[] _data;
10115       delete[] _title;
10116       _data = 0;
10117       _title = 0;
10118       if (_is_fullscreen) _desinit_fullscreen();
10119       _width = _height = _normalization = _window_width = _window_height = 0;
10120       _window_x = _window_y = 0;
10121       _is_fullscreen = false;
10122       _is_closed = true;
10123       _min = _max = 0;
10124       _title = 0;
10125       flush();
10126       return *this;
10127     }
10128 
10129     CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
10130                         const unsigned int normalization_type=3,
10131                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10132       if (!dimw || !dimh) return assign();
10133       _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
10134       _min = _max = 0;
10135       std::memset(_data,0,sizeof(unsigned int)*_width*_height);
10136       return paint();
10137     }
10138 
10139     template<typename T>
10140     CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
10141                         const unsigned int normalization_type=3,
10142                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10143       if (!img) return assign();
10144       CImg<T> tmp;
10145       const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
10146                                                                            (img._height - 1)/2,
10147                                                                            (img._depth - 1)/2));
10148       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
10149       if (_normalization==2) _min = (float)nimg.min_max(_max);
10150       return display(nimg);
10151     }
10152 
10153     template<typename T>
10154     CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
10155                         const unsigned int normalization_type=3,
10156                         const bool fullscreen_flag=false, const bool closed_flag=false) {
10157       if (!list) return assign();
10158       CImg<T> tmp;
10159       const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
10160                                                                                            (img._height - 1)/2,
10161                                                                                            (img._depth - 1)/2));
10162       _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
10163       if (_normalization==2) _min = (float)nimg.min_max(_max);
10164       return display(nimg);
10165     }
10166 
10167     CImgDisplay& assign(const CImgDisplay& disp) {
10168       if (!disp) return assign();
10169       _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
10170       std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
10171       return paint();
10172     }
10173 
10174     CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
10175       if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
10176       if (is_empty()) return assign(nwidth,nheight);
10177       const unsigned int
10178         tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
10179         tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
10180         dimx = tmpdimx?tmpdimx:1,
10181         dimy = tmpdimy?tmpdimy:1;
10182       if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
10183         if (_window_width!=dimx || _window_height!=dimy) {
10184           RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1;
10185           AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
10186           const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
10187           SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
10188         }
10189         if (_width!=dimx || _height!=dimy) {
10190           unsigned int *const ndata = new unsigned int[dimx*dimy];
10191           if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
10192           else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
10193           delete[] _data;
10194           _data = ndata;
10195           _bmi.bmiHeader.biWidth = (LONG)dimx;
10196           _bmi.bmiHeader.biHeight = -(int)dimy;
10197           _width = dimx;
10198           _height = dimy;
10199         }
10200         _window_width = dimx; _window_height = dimy;
10201         show();
10202       }
10203       _is_resized = false;
10204       if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2);
10205       if (force_redraw) return paint();
10206       return *this;
10207     }
10208 
10209     CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
10210       if (is_empty()) return *this;
10211       if (force_redraw) {
10212         const cimg_ulong buf_size = (cimg_ulong)_width*_height*4;
10213         void *odata = std::malloc(buf_size);
10214         if (odata) {
10215           std::memcpy(odata,_data,buf_size);
10216           assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
10217           std::memcpy(_data,odata,buf_size);
10218           std::free(odata);
10219         }
10220         return paint();
10221       }
10222       return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
10223     }
10224 
10225     CImgDisplay& show() {
10226       if (is_empty() || !_is_closed) return *this;
10227       _is_closed = false;
10228       if (_is_fullscreen) _init_fullscreen();
10229       ShowWindow(_window,SW_SHOW);
10230       _update_window_pos();
10231       return paint();
10232     }
10233 
10234     CImgDisplay& close() {
10235       if (is_empty() || _is_closed) return *this;
10236       _is_closed = true;
10237       if (_is_fullscreen) _desinit_fullscreen();
10238       ShowWindow(_window,SW_HIDE);
10239       _window_x = _window_y = 0;
10240       return *this;
10241     }
10242 
10243     CImgDisplay& move(const int posx, const int posy) {
10244       if (is_empty()) return *this;
10245       if (_window_x!=posx || _window_y!=posy) {
10246         if (!_is_fullscreen) {
10247           RECT rect;
10248           rect.left = rect.top = 0; rect.right = (LONG)_window_width - 1; rect.bottom = (LONG)_window_height - 1;
10249           AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
10250           const int
10251             border1 = (int)((rect.right - rect.left + 1 -_width)/2),
10252             border2 = (int)(rect.bottom - rect.top + 1 - _height - border1);
10253           SetWindowPos(_window,0,posx - border1,posy - border2,0,0,SWP_NOSIZE | SWP_NOZORDER);
10254         } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
10255         _window_x = posx;
10256         _window_y = posy;
10257         show();
10258       }
10259       _is_moved = false;
10260       return *this;
10261     }
10262 
10263     CImgDisplay& show_mouse() {
10264       if (is_empty()) return *this;
10265       _is_cursor_visible = true;
10266       return *this;
10267     }
10268 
10269     CImgDisplay& hide_mouse() {
10270       if (is_empty()) return *this;
10271       _is_cursor_visible = false;
10272       return *this;
10273     }
10274 
10275     CImgDisplay& set_mouse(const int posx, const int posy) {
10276       if (is_empty() || _is_closed || posx<0 || posy<0) return *this;
10277       _update_window_pos();
10278       const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
10279       if (res) { _mouse_x = posx; _mouse_y = posy; }
10280       return *this;
10281     }
10282 
10283     CImgDisplay& set_title(const char *const format, ...) {
10284       if (is_empty()) return *this;
10285       char *const tmp = new char[1024];
10286       va_list ap;
10287       va_start(ap, format);
10288       cimg_vsnprintf(tmp,1024,format,ap);
10289       va_end(ap);
10290       if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
10291       delete[] _title;
10292       const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
10293       _title = new char[s];
10294       std::memcpy(_title,tmp,s*sizeof(char));
10295       SetWindowTextA(_window, tmp);
10296       delete[] tmp;
10297       return *this;
10298     }
10299 
10300     template<typename T>
10301     CImgDisplay& display(const CImg<T>& img) {
10302       if (!img)
10303         throw CImgArgumentException(_cimgdisplay_instance
10304                                     "display(): Empty specified image.",
10305                                     cimgdisplay_instance);
10306       if (is_empty()) return assign(img);
10307       return render(img).paint();
10308     }
10309 
10310     CImgDisplay& paint() {
10311       if (_is_closed) return *this;
10312       WaitForSingleObject(_mutex,INFINITE);
10313       SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
10314       ReleaseMutex(_mutex);
10315       return *this;
10316     }
10317 
10318     template<typename T>
10319     CImgDisplay& render(const CImg<T>& img) {
10320       if (!img)
10321         throw CImgArgumentException(_cimgdisplay_instance
10322                                     "render(): Empty specified image.",
10323                                     cimgdisplay_instance);
10324 
10325       if (is_empty()) return *this;
10326       if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
10327                                                              (img._depth - 1)/2));
10328 
10329       const T
10330         *data1 = img._data,
10331         *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
10332         *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
10333 
10334       WaitForSingleObject(_mutex,INFINITE);
10335       unsigned int
10336         *const ndata = (img._width==_width && img._height==_height)?_data:
10337         new unsigned int[(size_t)img._width*img._height],
10338         *ptrd = ndata;
10339 
10340       if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
10341         _min = _max = 0;
10342         switch (img._spectrum) {
10343         case 1 : {
10344           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10345             const unsigned char val = (unsigned char)*(data1++);
10346             *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
10347           }
10348         } break;
10349         case 2 : {
10350           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10351             const unsigned char
10352               R = (unsigned char)*(data1++),
10353               G = (unsigned char)*(data2++);
10354             *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
10355           }
10356         } break;
10357         default : {
10358           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10359             const unsigned char
10360               R = (unsigned char)*(data1++),
10361               G = (unsigned char)*(data2++),
10362               B = (unsigned char)*(data3++);
10363             *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
10364           }
10365         }
10366         }
10367       } else {
10368         if (_normalization==3) {
10369           if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
10370           else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
10371         } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
10372         const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
10373         switch (img._spectrum) {
10374         case 1 : {
10375           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10376             const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
10377             *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
10378           }
10379         } break;
10380         case 2 : {
10381           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10382             const unsigned char
10383               R = (unsigned char)((*(data1++) - _min)*mm),
10384               G = (unsigned char)((*(data2++) - _min)*mm);
10385             *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
10386           }
10387         } break;
10388         default : {
10389           for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10390             const unsigned char
10391               R = (unsigned char)((*(data1++) - _min)*mm),
10392               G = (unsigned char)((*(data2++) - _min)*mm),
10393               B = (unsigned char)((*(data3++) - _min)*mm);
10394             *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
10395           }
10396         }
10397         }
10398       }
10399       if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
10400       ReleaseMutex(_mutex);
10401       return *this;
10402     }
10403 
10404     template<typename T>
10405     static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
10406       img.assign();
10407       HDC hScreen = GetDC(GetDesktopWindow());
10408       if (hScreen) {
10409         const int
10410           width = GetDeviceCaps(hScreen,HORZRES),
10411           height = GetDeviceCaps(hScreen,VERTRES);
10412         int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
10413         if (_x0>_x1) cimg::swap(_x0,_x1);
10414         if (_y0>_y1) cimg::swap(_y0,_y1);
10415         if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
10416           _x0 = std::max(_x0,0);
10417           _y0 = std::max(_y0,0);
10418           _x1 = std::min(_x1,width - 1);
10419           _y1 = std::min(_y1,height - 1);
10420           const int bw = _x1 - _x0 + 1, bh = _y1 - _y0 + 1;
10421           HDC hdcMem = CreateCompatibleDC(hScreen);
10422           if (hdcMem) {
10423             HBITMAP hBitmap = CreateCompatibleBitmap(hScreen,bw,bh);
10424             if (hBitmap) {
10425               HGDIOBJ hOld = SelectObject(hdcMem,hBitmap);
10426               if (hOld && BitBlt(hdcMem,0,0,bw,bh,hScreen,_x0,_y0,SRCCOPY) && SelectObject(hdcMem,hOld)) {
10427                 BITMAPINFOHEADER bmi;
10428                 bmi.biSize = sizeof(BITMAPINFOHEADER);
10429                 bmi.biWidth = bw;
10430                 bmi.biHeight = -bh;
10431                 bmi.biPlanes = 1;
10432                 bmi.biBitCount = 32;
10433                 bmi.biCompression = BI_RGB;
10434                 bmi.biSizeImage = 0;
10435                 bmi.biXPelsPerMeter = bmi.biYPelsPerMeter = 0;
10436                 bmi.biClrUsed = bmi.biClrImportant = 0;
10437                 unsigned char *buf = new unsigned char[4*bw*bh];
10438                 if (GetDIBits(hdcMem,hBitmap,0,bh,buf,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)) {
10439                   img.assign(bw,bh,1,3);
10440                   const unsigned char *ptrs = buf;
10441                   T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
10442                   cimg_forXY(img,x,y) {
10443                     *(pR++) = (T)ptrs[2];
10444                     *(pG++) = (T)ptrs[1];
10445                     *(pB++) = (T)ptrs[0];
10446                     ptrs+=4;
10447                   }
10448                 }
10449                 delete[] buf;
10450               }
10451               DeleteObject(hBitmap);
10452             }
10453             DeleteDC(hdcMem);
10454           }
10455         }
10456         ReleaseDC(GetDesktopWindow(),hScreen);
10457       }
10458       if (img.is_empty())
10459         throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
10460                                    "with coordinates (%d,%d)-(%d,%d).",
10461                                    x0,y0,x1,y1);
10462     }
10463 
10464     template<typename T>
10465     const CImgDisplay& snapshot(CImg<T>& img) const {
10466       if (is_empty()) { img.assign(); return *this; }
10467       const unsigned int *ptrs = _data;
10468       img.assign(_width,_height,1,3);
10469       T
10470         *data1 = img.data(0,0,0,0),
10471         *data2 = img.data(0,0,0,1),
10472         *data3 = img.data(0,0,0,2);
10473       for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
10474         const unsigned int val = *(ptrs++);
10475         *(data1++) = (T)(unsigned char)(val>>16);
10476         *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
10477         *(data3++) = (T)(unsigned char)(val&0xFF);
10478       }
10479       return *this;
10480     }
10481 #endif
10482 
10483     //@}
10484   };
10485 
10486   /*
10487    #--------------------------------------
10488    #
10489    #
10490    #
10491    # Definition of the CImg<T> structure
10492    #
10493    #
10494    #
10495    #--------------------------------------
10496    */
10497 
10498   //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
10499   /**
10500      This is the main class of the %CImg Library. It declares and constructs
10501      an image, allows access to its pixel values, and is able to perform various image operations.
10502 
10503      \par Image representation
10504 
10505      A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels,
10506      each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth
10507      and number of channels.
10508      Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>,
10509      while the number of channels is rather used as a vector-valued dimension
10510      (it may describe the R,G,B color channels for instance).
10511      If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>.
10512 
10513      Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels,
10514      as well as images with less dimensions (1d scalar signal, 2d color images, ...).
10515      Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
10516 
10517      Concerning the pixel value type \c T:
10518      fully supported template types are the basic C++ types: <tt>unsigned char, char, short, unsigned int, int,
10519      unsigned long, long, float, double, ... </tt>.
10520      Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
10521      while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
10522      images that have floating-point pixel values. The default value for the template T is \c float.
10523      Using your own template types may be possible. However, you will certainly have to define the complete set
10524      of arithmetic and logical operators for your class.
10525 
10526      \par Image structure
10527 
10528      The \c CImg<T> structure contains \e six fields:
10529      - \c _width defines the number of \a columns of the image (size along the X-axis).
10530      - \c _height defines the number of \a rows of the image (size along the Y-axis).
10531      - \c _depth defines the number of \a slices of the image (size along the Z-axis).
10532      - \c _spectrum defines the number of \a channels of the image (size along the C-axis).
10533      - \c _data defines a \a pointer to the \a pixel \a data (of type \c T).
10534      - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with
10535        another image.
10536 
10537      You can access these fields publicly although it is recommended to use the dedicated functions
10538      width(), height(), depth(), spectrum() and ptr() to do so.
10539      Image dimensions are not limited to a specific range (as long as you got enough available memory).
10540      A value of \e 1 usually means that the corresponding dimension is \a flat.
10541      If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
10542      Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
10543      (a CImgInstanceException will be thrown instead).
10544      Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
10545 
10546      \par Image declaration and construction
10547 
10548      Declaring an image can be done by using one of the several available constructors.
10549      Here is a list of the most used:
10550 
10551      - Construct images from arbitrary dimensions:
10552          - <tt>CImg<char> img;</tt> declares an empty image.
10553          - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
10554          \c unsigned \c char pixel values.
10555          - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
10556          - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
10557          (colors are stored as an image with three channels).
10558          - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
10559          (with \c double pixel values).
10560          - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
10561          (with \c float pixels, which is the default value of the template parameter \c T).
10562          - \b Note: images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to
10563          do it, or use the specific constructor taking 5 parameters like this:
10564          <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
10565 
10566      - Construct images from filenames:
10567          - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
10568          - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the
10569          file "analyze.hdr".
10570          - \b Note: You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
10571          to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
10572 
10573      - Construct images from C-style arrays:
10574          - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
10575          \c data_buffer (of size 256x256=65536).
10576          - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3);</tt> constructs a 256x256 color image
10577          from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
10578 
10579          The complete list of constructors can be found <a href="#constructors">here</a>.
10580 
10581      \par Most useful functions
10582 
10583      The \c CImg<T> class contains a lot of functions that operates on images.
10584      Some of the most useful are:
10585 
10586      - operator()(): Read or write pixel values.
10587      - display(): displays the image in a new window.
10588   **/
10589   template<typename T>
10590   struct CImg {
10591 
10592     unsigned int _width, _height, _depth, _spectrum;
10593     bool _is_shared;
10594     T *_data;
10595 
10596     //! Simple iterator type, to loop through each pixel value of an image instance.
10597     /**
10598        \note
10599        - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>.
10600        - You will seldom have to use iterators in %CImg, most classical operations
10601          being achieved (often in a faster way) using methods of \c CImg<T>.
10602        \par Example
10603        \code
10604        CImg<float> img("reference.jpg");                                         // Load image from file.
10605        // Set all pixels to '0', with a CImg iterator.
10606        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0;
10607        img.fill(0);                                                              // Do the same with a built-in method.
10608        \endcode
10609    **/
10610     typedef T* iterator;
10611 
10612     //! Simple const iterator type, to loop through each pixel value of a \c const image instance.
10613     /**
10614        \note
10615        - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*.
10616        - You will seldom have to use iterators in %CImg, most classical operations
10617          being achieved (often in a faster way) using methods of \c CImg<T>.
10618        \par Example
10619        \code
10620        const CImg<float> img("reference.jpg");                                    // Load image from file.
10621        float sum = 0;
10622        // Compute sum of all pixel values, with a CImg iterator.
10623        for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it;
10624        const float sum2 = img.sum();                                              // Do the same with a built-in method.
10625        \endcode
10626     **/
10627     typedef const T* const_iterator;
10628 
10629     //! Pixel value type.
10630     /**
10631        Refer to the type of the pixel values of an image instance.
10632        \note
10633        - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T.
10634        - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
10635          compatibility with STL naming conventions.
10636     **/
10637     typedef T value_type;
10638 
10639     // Define common types related to template type T.
10640     typedef typename cimg::superset<T,bool>::type Tbool;
10641     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
10642     typedef typename cimg::superset<T,char>::type Tchar;
10643     typedef typename cimg::superset<T,unsigned short>::type Tushort;
10644     typedef typename cimg::superset<T,short>::type Tshort;
10645     typedef typename cimg::superset<T,unsigned int>::type Tuint;
10646     typedef typename cimg::superset<T,int>::type Tint;
10647     typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
10648     typedef typename cimg::superset<T,cimg_long>::type Tlong;
10649     typedef typename cimg::superset<T,float>::type Tfloat;
10650     typedef typename cimg::superset<T,double>::type Tdouble;
10651     typedef typename cimg::last<T,bool>::type boolT;
10652     typedef typename cimg::last<T,unsigned char>::type ucharT;
10653     typedef typename cimg::last<T,char>::type charT;
10654     typedef typename cimg::last<T,unsigned short>::type ushortT;
10655     typedef typename cimg::last<T,short>::type shortT;
10656     typedef typename cimg::last<T,unsigned int>::type uintT;
10657     typedef typename cimg::last<T,int>::type intT;
10658     typedef typename cimg::last<T,cimg_ulong>::type ulongT;
10659     typedef typename cimg::last<T,cimg_long>::type longT;
10660     typedef typename cimg::last<T,cimg_uint64>::type uint64T;
10661     typedef typename cimg::last<T,cimg_int64>::type int64T;
10662     typedef typename cimg::last<T,float>::type floatT;
10663     typedef typename cimg::last<T,double>::type doubleT;
10664 
10665     //@}
10666     //---------------------------
10667     //
10668     //! \name Plugins
10669     //@{
10670     //---------------------------
10671 #ifdef cimg_plugin
10672 #include cimg_plugin
10673 #endif
10674 #ifdef cimg_plugin1
10675 #include cimg_plugin1
10676 #endif
10677 #ifdef cimg_plugin2
10678 #include cimg_plugin2
10679 #endif
10680 #ifdef cimg_plugin3
10681 #include cimg_plugin3
10682 #endif
10683 #ifdef cimg_plugin4
10684 #include cimg_plugin4
10685 #endif
10686 #ifdef cimg_plugin5
10687 #include cimg_plugin5
10688 #endif
10689 #ifdef cimg_plugin6
10690 #include cimg_plugin6
10691 #endif
10692 #ifdef cimg_plugin7
10693 #include cimg_plugin7
10694 #endif
10695 #ifdef cimg_plugin8
10696 #include cimg_plugin8
10697 #endif
10698 
10699     //@}
10700     //---------------------------------------------------------
10701     //
10702     //! \name Constructors / Destructor / Instance Management
10703     //@{
10704     //---------------------------------------------------------
10705 
10706     //! Destroy image.
10707     /**
10708        \note
10709        - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances.
10710        - Destroying an empty or shared image does nothing actually.
10711        \warning
10712        - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image
10713          that shares its buffer with the destroyed instance, in order to avoid further invalid memory access
10714          (to a deallocated buffer).
10715     **/
10716     ~CImg() {
10717       if (!_is_shared) delete[] _data;
10718     }
10719 
10720     //! Construct empty image.
10721     /**
10722        \note
10723        - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum()
10724          are set to \c 0, as well as its pixel buffer pointer data().
10725        - An empty image may be re-assigned afterwards, e.g. with the family of
10726          assign(unsigned int,unsigned int,unsigned int,unsigned int) methods,
10727          or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T.
10728        - An empty image is never shared.
10729        \par Example
10730        \code
10731        CImg<float> img1, img2;      // Construct two empty images.
10732        img1.assign(256,256,1,3);    // Re-assign 'img1' to be a 256x256x1x3 (color) image.
10733        img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'.
10734        img2.assign();               // Re-assign 'img2' to be an empty image again.
10735        \endcode
10736     **/
10737     CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
10738 
10739     //! Construct image with specified size.
10740     /**
10741        \param size_x Image width().
10742        \param size_y Image height().
10743        \param size_z Image depth().
10744        \param size_c Image spectrum() (number of channels).
10745        \note
10746        - It is able to create only \e non-shared images, and allocates thus a pixel buffer data()
10747          for each constructed image instance.
10748        - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of
10749          an \e empty image.
10750        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
10751          (e.g. when requested size is too big for available memory).
10752        \warning
10753        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
10754          In order to initialize pixel values during construction (e.g. with \c 0), use constructor
10755          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead.
10756        \par Example
10757        \code
10758        CImg<float> img1(256,256,1,3);   // Construct a 256x256x1x3 (color) image, filled with garbage values.
10759        CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'.
10760        \endcode
10761     **/
10762     explicit CImg(const unsigned int size_x, const unsigned int size_y=1,
10763                   const unsigned int size_z=1, const unsigned int size_c=1):
10764       _is_shared(false) {
10765       size_t siz = (size_t)size_x*size_y*size_z*size_c;
10766       if (siz) {
10767         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
10768         try { _data = new T[siz]; } catch (...) {
10769           _width = _height = _depth = _spectrum = 0; _data = 0;
10770           throw CImgInstanceException(_cimg_instance
10771                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
10772                                       cimg_instance,
10773                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
10774                                       size_x,size_y,size_z,size_c);
10775         }
10776       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
10777     }
10778 
10779     //! Construct image with specified size and initialize pixel values.
10780     /**
10781        \param size_x Image width().
10782        \param size_y Image height().
10783        \param size_z Image depth().
10784        \param size_c Image spectrum() (number of channels).
10785        \param value Initialization value.
10786        \note
10787        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int),
10788          but it also fills the pixel buffer with the specified \c value.
10789        \warning
10790        - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels
10791          (e.g. RGB vector, for color images).
10792          For this task, you may use fillC() after construction.
10793     **/
10794     CImg(const unsigned int size_x, const unsigned int size_y,
10795          const unsigned int size_z, const unsigned int size_c, const T& value):
10796       _is_shared(false) {
10797       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
10798       if (siz) {
10799         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
10800         try { _data = new T[siz]; } catch (...) {
10801           _width = _height = _depth = _spectrum = 0; _data = 0;
10802           throw CImgInstanceException(_cimg_instance
10803                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
10804                                       cimg_instance,
10805                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
10806                                       size_x,size_y,size_z,size_c);
10807         }
10808         fill(value);
10809       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
10810     }
10811 
10812     //! Construct image with specified size and initialize pixel values from a sequence of integers.
10813     /**
10814        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
10815        with pixels of type \c T, and initialize pixel
10816        values from the specified sequence of integers \c value0,\c value1,\c ...
10817        \param size_x Image width().
10818        \param size_y Image height().
10819        \param size_z Image depth().
10820        \param size_c Image spectrum() (number of channels).
10821        \param value0 First value of the initialization sequence (must be an \e integer).
10822        \param value1 Second value of the initialization sequence (must be an \e integer).
10823        \param ...
10824        \note
10825        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
10826          the pixel buffer with a sequence of specified integer values.
10827        \warning
10828        - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence.
10829          Otherwise, the constructor may crash or fill your image pixels with garbage.
10830        \par Example
10831        \code
10832        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image.
10833                              0,255,0,255,  // Set the 4 values for the red component.
10834                              0,0,255,255,  // Set the 4 values for the green component.
10835                              64,64,64,64); // Set the 4 values for the blue component.
10836        img.resize(150,150).display();
10837        \endcode
10838        \image html ref_constructor1.jpg
10839      **/
10840     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
10841          const int value0, const int value1, ...):
10842       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10843 #define _CImg_stdarg(img,a0,a1,N,t) { \
10844 	size_t _siz = (size_t)N; \
10845 	if (_siz--) { \
10846 	  va_list ap; \
10847 	  va_start(ap,a1); \
10848 	  T *ptrd = (img)._data; \
10849 	  *(ptrd++) = (T)a0; \
10850 	  if (_siz--) { \
10851 	    *(ptrd++) = (T)a1; \
10852 	    for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
10853 	  } \
10854 	  va_end(ap); \
10855 	} \
10856       }
10857       assign(size_x,size_y,size_z,size_c);
10858       _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int);
10859     }
10860 
10861 #if cimg_use_cpp11==1
10862     //! Construct image with specified size and initialize pixel values from an initializer list of integers.
10863     /**
10864        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
10865        with pixels of type \c T, and initialize pixel
10866        values from the specified initializer list of integers { \c value0,\c value1,\c ... }
10867        \param size_x Image width().
10868        \param size_y Image height().
10869        \param size_z Image depth().
10870        \param size_c Image spectrum() (number of channels).
10871        \param { value0, value1, ... } Initialization list
10872        \param repeat_values Tells if the value filling process is repeated over the image.
10873 
10874        \note
10875        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
10876          the pixel buffer with a sequence of specified integer values.
10877        \par Example
10878        \code
10879        const CImg<float> img(2,2,1,3,      // Construct a 2x2 color (RGB) image.
10880                              { 0,255,0,255,    // Set the 4 values for the red component.
10881                                0,0,255,255,    // Set the 4 values for the green component.
10882                                64,64,64,64 }); // Set the 4 values for the blue component.
10883        img.resize(150,150).display();
10884        \endcode
10885        \image html ref_constructor1.jpg
10886     **/
10887     template<typename t>
10888     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
10889          const std::initializer_list<t> values,
10890 	 const bool repeat_values=true):
10891       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10892 #define _cimg_constructor_cpp11(repeat_values) \
10893   auto it = values.begin(); \
10894   size_t siz = size(); \
10895   if (repeat_values) for (T *ptrd = _data; siz--; ) { \
10896     *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \
10897   else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); }
10898       assign(size_x,size_y,size_z,size_c);
10899       _cimg_constructor_cpp11(repeat_values);
10900     }
10901 
10902     template<typename t>
10903     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z,
10904          std::initializer_list<t> values,
10905 	 const bool repeat_values=true):
10906       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10907       assign(size_x,size_y,size_z);
10908       _cimg_constructor_cpp11(repeat_values);
10909     }
10910 
10911     template<typename t>
10912     CImg(const unsigned int size_x, const unsigned int size_y,
10913          std::initializer_list<t> values,
10914 	 const bool repeat_values=true):
10915       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10916       assign(size_x,size_y);
10917       _cimg_constructor_cpp11(repeat_values);
10918     }
10919 
10920     template<typename t>
10921     CImg(const unsigned int size_x,
10922          std::initializer_list<t> values,
10923          const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10924       assign(size_x);
10925       _cimg_constructor_cpp11(repeat_values);
10926     }
10927 
10928     //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers.
10929     /**
10930        Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1,
10931        with pixels of type \c T, and initialize pixel
10932        values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is
10933        given by the size of the initializer list.
10934        \param { value0, value1, ... } Initialization list
10935        \note
10936        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1,
10937          but it also fills the pixel buffer with a sequence of specified integer values.
10938        \par Example
10939        \code
10940        const CImg<float> img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values.
10941        img.resize(150,150).display();
10942        \endcode
10943        \image html ref_constructor1.jpg
10944      **/
10945     template<typename t>
10946     CImg(const std::initializer_list<t> values):
10947       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10948       assign(values.size(),1,1,1);
10949       auto it = values.begin();
10950       unsigned int siz = _width;
10951       for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++));
10952     }
10953 
10954     template<typename t>
10955     CImg<T> & operator=(std::initializer_list<t> values) {
10956       _cimg_constructor_cpp11(siz>values.size());
10957       return *this;
10958     }
10959 #endif
10960 
10961     //! Construct image with specified size and initialize pixel values from a sequence of doubles.
10962     /**
10963        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T,
10964        and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ...
10965        \param size_x Image width().
10966        \param size_y Image height().
10967        \param size_z Image depth().
10968        \param size_c Image spectrum() (number of channels).
10969        \param value0 First value of the initialization sequence (must be a \e double).
10970        \param value1 Second value of the initialization sequence (must be a \e double).
10971        \param ...
10972        \note
10973        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but
10974          takes a sequence of double values instead of integers.
10975        \warning
10976        - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence.
10977          Otherwise, the constructor may crash or fill your image with garbage.
10978          For instance, the code below will probably crash on most platforms:
10979          \code
10980          const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'!
10981          \endcode
10982      **/
10983     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
10984          const double value0, const double value1, ...):
10985       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
10986       assign(size_x,size_y,size_z,size_c);
10987       _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double);
10988     }
10989 
10990     //! Construct image with specified size and initialize pixel values from a value string.
10991     /**
10992        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T,
10993        and initializes pixel values from the specified string \c values.
10994        \param size_x Image width().
10995        \param size_y Image height().
10996        \param size_z Image depth().
10997        \param size_c Image spectrum() (number of channels).
10998        \param values Value string describing the way pixel values are set.
10999        \param repeat_values Tells if the value filling process is repeated over the image.
11000        \note
11001        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
11002          the pixel buffer with values described in the value string \c values.
11003        - Value string \c values may describe two different filling processes:
11004          - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>.
11005            In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence.
11006          - Either, \c values is a formula, as in <tt>"cos(x/10)*sin(y/20)"</tt>.
11007            In this case, parameter \c repeat_values is pointless.
11008        - For both cases, specifying \c repeat_values is mandatory.
11009          It disambiguates the possible overloading of constructor
11010          CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>.
11011        - A \c CImgArgumentException is thrown when an invalid value string \c values is specified.
11012        \par Example
11013        \code
11014        const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence.
11015                          img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula.
11016        (img1,img2).display();
11017        \endcode
11018        \image html ref_constructor2.jpg
11019      **/
11020     CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
11021 	 const char *const values, const bool repeat_values):_is_shared(false) {
11022       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11023       if (siz) {
11024         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11025         try { _data = new T[siz]; } catch (...) {
11026           _width = _height = _depth = _spectrum = 0; _data = 0;
11027           throw CImgInstanceException(_cimg_instance
11028                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11029                                       cimg_instance,
11030                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11031                                       size_x,size_y,size_z,size_c);
11032         }
11033         fill(values,repeat_values);
11034       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11035     }
11036 
11037     //! Construct image with specified size and initialize pixel values from a memory buffer.
11038     /**
11039        Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T,
11040        and initializes pixel values from the specified \c t* memory buffer.
11041        \param values Pointer to the input memory buffer.
11042        \param size_x Image width().
11043        \param size_y Image height().
11044        \param size_z Image depth().
11045        \param size_c Image spectrum() (number of channels).
11046        \param is_shared Tells if input memory buffer must be shared by the current instance.
11047        \note
11048        - If \c is_shared is \c false, the image instance allocates its own pixel buffer,
11049          and values from the specified input buffer are copied to the instance buffer.
11050          If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy.
11051        - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its
11052          own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared
11053          image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator.
11054        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
11055          (e.g. when requested size is too big for available memory).
11056        \warning
11057        - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data()
11058          (e.g. already deallocated).
11059        \par Example
11060        \code
11061        unsigned char tab[256*256] = { 0 };
11062        CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'.
11063                            img2(tab,256,256,1,1,true);  // Construct new shared-image from buffer 'tab'.
11064        tab[1024] = 255;                                 // Here, 'img2' is indirectly modified, but not 'img1'.
11065        \endcode
11066     **/
11067     template<typename t>
11068     CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
11069          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
11070       if (is_shared) {
11071         _width = _height = _depth = _spectrum = 0; _data = 0;
11072         throw CImgArgumentException(_cimg_instance
11073                                     "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance "
11074                                     "from a (%s*) buffer (pixel types are different).",
11075                                     cimg_instance,
11076                                     size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
11077       }
11078       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11079       if (values && siz) {
11080         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11081         try { _data = new T[siz]; } catch (...) {
11082           _width = _height = _depth = _spectrum = 0; _data = 0;
11083           throw CImgInstanceException(_cimg_instance
11084                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11085                                       cimg_instance,
11086                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11087                                       size_x,size_y,size_z,size_c);
11088 
11089         }
11090         const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
11091       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11092     }
11093 
11094     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
11095     CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
11096          const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
11097       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11098       if (values && siz) {
11099         _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
11100         if (_is_shared) _data = const_cast<T*>(values);
11101         else {
11102           try { _data = new T[siz]; } catch (...) {
11103             _width = _height = _depth = _spectrum = 0; _data = 0;
11104             throw CImgInstanceException(_cimg_instance
11105                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11106                                         cimg_instance,
11107                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11108                                         size_x,size_y,size_z,size_c);
11109           }
11110           std::memcpy(_data,values,siz*sizeof(T));
11111         }
11112       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
11113     }
11114 
11115     //! Construct image from reading an image file.
11116     /**
11117        Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from
11118        an image file.
11119        \param filename Filename, as a C-string.
11120        \note
11121        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image
11122          dimensions and pixel values from the specified image file.
11123        - The recognition of the image file format by %CImg higly depends on the tools installed on your system
11124          and on the external libraries you used to link your code against.
11125        - Considered pixel type \c T should better fit the file format specification, or data loss may occur during
11126          file load (e.g. constructing a \c CImg<unsigned char> from a float-valued image file).
11127        - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not
11128          recognized.
11129        \par Example
11130        \code
11131        const CImg<float> img("reference.jpg");
11132        img.display();
11133        \endcode
11134        \image html ref_image.jpg
11135     **/
11136     explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11137       assign(filename);
11138     }
11139 
11140     //! Construct image copy.
11141     /**
11142        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance.
11143        \param img Input image to copy.
11144        \note
11145        - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the
11146          input image \c img.
11147        - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also
11148          \e shared, and shares its pixel buffer with \c img.
11149          Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img.
11150          This behavior is needful to allow functions to return shared images.
11151        - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input
11152          image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and
11153          \c t are different.
11154        - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than
11155          with different types.
11156        - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
11157          (e.g. not enough available memory).
11158     **/
11159     template<typename t>
11160     CImg(const CImg<t>& img):_is_shared(false) {
11161       const size_t siz = (size_t)img.size();
11162       if (img._data && siz) {
11163         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
11164         try { _data = new T[siz]; } catch (...) {
11165           _width = _height = _depth = _spectrum = 0; _data = 0;
11166           throw CImgInstanceException(_cimg_instance
11167                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11168                                       cimg_instance,
11169                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
11170                                       img._width,img._height,img._depth,img._spectrum);
11171         }
11172         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
11173       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11174     }
11175 
11176     //! Construct image copy \specialization.
11177     CImg(const CImg<T>& img) {
11178       const size_t siz = (size_t)img.size();
11179       if (img._data && siz) {
11180         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
11181         _is_shared = img._is_shared;
11182         if (_is_shared) _data = const_cast<T*>(img._data);
11183         else {
11184           try { _data = new T[siz]; } catch (...) {
11185             _width = _height = _depth = _spectrum = 0; _data = 0;
11186             throw CImgInstanceException(_cimg_instance
11187                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11188                                         cimg_instance,
11189                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
11190                                         img._width,img._height,img._depth,img._spectrum);
11191 
11192           }
11193           std::memcpy(_data,img._data,siz*sizeof(T));
11194         }
11195       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
11196     }
11197 
11198     //! Advanced copy constructor.
11199     /**
11200        Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance,
11201        while forcing the shared state of the constructed copy.
11202        \param img Input image to copy.
11203        \param is_shared Tells about the shared state of the constructed copy.
11204        \note
11205        - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of
11206          the constructed image, which does not depend anymore on the shared state of the input image \c img:
11207          - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img.
11208            For that case, the pixel types \c T and \c t \e must be the same.
11209          - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input
11210            image \c img is shared or not.
11211        - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t.
11212     **/
11213     template<typename t>
11214     CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
11215       if (is_shared) {
11216         _width = _height = _depth = _spectrum = 0; _data = 0;
11217         throw CImgArgumentException(_cimg_instance
11218                                     "CImg(): Invalid construction request of a shared instance from a "
11219                                     "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
11220                                     cimg_instance,
11221                                     CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
11222       }
11223       const size_t siz = (size_t)img.size();
11224       if (img._data && siz) {
11225         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
11226         try { _data = new T[siz]; } catch (...) {
11227           _width = _height = _depth = _spectrum = 0; _data = 0;
11228           throw CImgInstanceException(_cimg_instance
11229                                       "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11230                                       cimg_instance,
11231                                       cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
11232                                       img._width,img._height,img._depth,img._spectrum);
11233         }
11234         const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
11235       } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
11236     }
11237 
11238     //! Advanced copy constructor \specialization.
11239     CImg(const CImg<T>& img, const bool is_shared) {
11240       const size_t siz = (size_t)img.size();
11241       if (img._data && siz) {
11242         _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
11243         _is_shared = is_shared;
11244         if (_is_shared) _data = const_cast<T*>(img._data);
11245         else {
11246           try { _data = new T[siz]; } catch (...) {
11247             _width = _height = _depth = _spectrum = 0; _data = 0;
11248             throw CImgInstanceException(_cimg_instance
11249                                         "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11250                                         cimg_instance,
11251                                         cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
11252                                         img._width,img._height,img._depth,img._spectrum);
11253           }
11254           std::memcpy(_data,img._data,siz*sizeof(T));
11255         }
11256       } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
11257     }
11258 
11259     //! Construct image with dimensions borrowed from another image.
11260     /**
11261        Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing
11262        \c CImg<t> instance.
11263        \param img Input image from which dimensions are borrowed.
11264        \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions.
11265        \note
11266        - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions
11267          (\e not its pixel values) from an existing \c CImg<t> instance.
11268        - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
11269          In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T)
11270          instead.
11271        \par Example
11272        \code
11273        const CImg<float> img1(256,128,1,3),      // 'img1' is a 256x128x1x3 image.
11274                          img2(img1,"xyzc"),      // 'img2' is a 256x128x1x3 image.
11275                          img3(img1,"y,x,z,c"),   // 'img3' is a 128x256x1x3 image.
11276                          img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0').
11277        \endcode
11278      **/
11279     template<typename t>
11280     CImg(const CImg<t>& img, const char *const dimensions):
11281       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11282       assign(img,dimensions);
11283     }
11284 
11285     //! Construct image with dimensions borrowed from another image and initialize pixel values.
11286     /**
11287        Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing
11288        \c CImg<t> instance, and set all pixel values to specified \c value.
11289        \param img Input image from which dimensions are borrowed.
11290        \param dimensions String describing the image size along the X,Y,Z and V-dimensions.
11291        \param value Value used for initialization.
11292        \note
11293        - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value.
11294      **/
11295     template<typename t>
11296     CImg(const CImg<t>& img, const char *const dimensions, const T& value):
11297       _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11298       assign(img,dimensions).fill(value);
11299     }
11300 
11301     //! Construct image from a display window.
11302     /**
11303        Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance.
11304        \param disp Input display window.
11305        \note
11306        - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay.
11307        - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3
11308          (i.e. a 2d color image).
11309        - The image pixels are read as 8-bits RGB values.
11310      **/
11311     explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11312       disp.snapshot(*this);
11313     }
11314 
11315     // Constructor and assignment operator for rvalue references (c++11).
11316     // This avoids an additional image copy for methods returning new images. Can save RAM for big images !
11317 #if cimg_use_cpp11==1
11318     CImg(CImg<T>&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
11319       swap(img);
11320     }
11321     CImg<T>& operator=(CImg<T>&& img) {
11322       if (_is_shared) return assign(img);
11323       return img.swap(*this);
11324     }
11325 #endif
11326 
11327     //! Construct empty image \inplace.
11328     /**
11329        In-place version of the default constructor CImg(). It simply resets the instance to an empty image.
11330     **/
11331     CImg<T>& assign() {
11332       if (!_is_shared) delete[] _data;
11333       _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
11334       return *this;
11335     }
11336 
11337     //! Construct image with specified size \inplace.
11338     /**
11339        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int).
11340     **/
11341     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1,
11342                     const unsigned int size_z=1, const unsigned int size_c=1) {
11343       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11344       if (!siz) return assign();
11345       const size_t curr_siz = (size_t)size();
11346       if (siz!=curr_siz) {
11347 	if (_is_shared)
11348           throw CImgArgumentException(_cimg_instance
11349                                       "assign(): Invalid assignement request of shared instance from specified "
11350                                       "image (%u,%u,%u,%u).",
11351                                       cimg_instance,
11352                                       size_x,size_y,size_z,size_c);
11353 	else {
11354           delete[] _data;
11355           try { _data = new T[siz]; } catch (...) {
11356             _width = _height = _depth = _spectrum = 0; _data = 0;
11357             throw CImgInstanceException(_cimg_instance
11358                                         "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11359                                         cimg_instance,
11360                                         cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11361                                         size_x,size_y,size_z,size_c);
11362           }
11363         }
11364       }
11365       _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11366       return *this;
11367     }
11368 
11369     //! Construct image with specified size and initialize pixel values \inplace.
11370     /**
11371        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
11372     **/
11373     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
11374                     const unsigned int size_z, const unsigned int size_c, const T& value) {
11375       return assign(size_x,size_y,size_z,size_c).fill(value);
11376     }
11377 
11378     //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace.
11379     /**
11380        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
11381     **/
11382     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
11383                     const unsigned int size_z, const unsigned int size_c,
11384                     const int value0, const int value1, ...) {
11385       assign(size_x,size_y,size_z,size_c);
11386       _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,int);
11387       return *this;
11388     }
11389 
11390     //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace.
11391     /**
11392        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
11393     **/
11394     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
11395                     const unsigned int size_z, const unsigned int size_c,
11396                     const double value0, const double value1, ...) {
11397       assign(size_x,size_y,size_z,size_c);
11398       _CImg_stdarg(*this,value0,value1,(size_t)size_x*size_y*size_z*size_c,double);
11399       return *this;
11400     }
11401 
11402     //! Construct image with specified size and initialize pixel values from a value string \inplace.
11403     /**
11404        In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
11405     **/
11406     CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
11407                     const unsigned int size_z, const unsigned int size_c,
11408                     const char *const values, const bool repeat_values) {
11409       return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
11410     }
11411 
11412     //! Construct image with specified size and initialize pixel values from a memory buffer \inplace.
11413     /**
11414        In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int).
11415     **/
11416     template<typename t>
11417     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
11418                     const unsigned int size_z=1, const unsigned int size_c=1) {
11419       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11420       if (!values || !siz) return assign();
11421       assign(size_x,size_y,size_z,size_c);
11422       const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
11423       return *this;
11424     }
11425 
11426     //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
11427     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
11428                     const unsigned int size_z=1, const unsigned int size_c=1) {
11429       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11430       if (!values || !siz) return assign();
11431       const size_t curr_siz = (size_t)size();
11432       if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
11433       if (_is_shared || values + siz<_data || values>=_data + size()) {
11434         assign(size_x,size_y,size_z,size_c);
11435         if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T));
11436         else std::memcpy((void*)_data,(void*)values,siz*sizeof(T));
11437       } else {
11438         T *new_data = 0;
11439         try { new_data = new T[siz]; } catch (...) {
11440           _width = _height = _depth = _spectrum = 0; _data = 0;
11441           throw CImgInstanceException(_cimg_instance
11442                                       "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
11443                                       cimg_instance,
11444                                       cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
11445                                       size_x,size_y,size_z,size_c);
11446         }
11447         std::memcpy((void*)new_data,(void*)values,siz*sizeof(T));
11448         delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
11449       }
11450       return *this;
11451     }
11452 
11453     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
11454     template<typename t>
11455     CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
11456                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
11457       if (is_shared)
11458         throw CImgArgumentException(_cimg_instance
11459                                     "assign(): Invalid assignment request of shared instance from (%s*) buffer"
11460                                     "(pixel types are different).",
11461                                     cimg_instance,
11462                                     CImg<t>::pixel_type());
11463       return assign(values,size_x,size_y,size_z,size_c);
11464     }
11465 
11466     //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
11467     CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
11468                     const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
11469       const size_t siz = (size_t)size_x*size_y*size_z*size_c;
11470       if (!values || !siz) return assign();
11471       if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
11472       else {
11473 	if (!_is_shared) {
11474 	  if (values + siz<_data || values>=_data + size()) assign();
11475 	  else cimg::warn(_cimg_instance
11476                           "assign(): Shared image instance has overlapping memory.",
11477                           cimg_instance);
11478 	}
11479 	_width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
11480 	_data = const_cast<T*>(values);
11481       }
11482       return *this;
11483     }
11484 
11485     //! Construct image from reading an image file \inplace.
11486     /**
11487        In-place version of the constructor CImg(const char*).
11488     **/
11489     CImg<T>& assign(const char *const filename) {
11490       return load(filename);
11491     }
11492 
11493     //! Construct image copy \inplace.
11494     /**
11495        In-place version of the constructor CImg(const CImg<t>&).
11496     **/
11497     template<typename t>
11498     CImg<T>& assign(const CImg<t>& img) {
11499       return assign(img._data,img._width,img._height,img._depth,img._spectrum);
11500     }
11501 
11502     //! In-place version of the advanced copy constructor.
11503     /**
11504        In-place version of the constructor CImg(const CImg<t>&,bool).
11505      **/
11506     template<typename t>
11507     CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
11508       return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
11509     }
11510 
11511     //! Construct image with dimensions borrowed from another image \inplace.
11512     /**
11513        In-place version of the constructor CImg(const CImg<t>&,const char*).
11514     **/
11515     template<typename t>
11516     CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
11517       if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
11518       unsigned int siz[4] = { 0,1,1,1 }, k = 0;
11519       CImg<charT> item(256);
11520       for (const char *s = dimensions; *s && k<4; ++k) {
11521         if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item);
11522         if (*s) {
11523           unsigned int val = 0; char sep = 0;
11524           if (cimg_sscanf(s,"%u%c",&val,&sep)>0) {
11525             if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
11526             else siz[k] = val;
11527             while (*s>='0' && *s<='9') ++s;
11528             if (sep=='%') ++s;
11529           } else switch (cimg::lowercase(*s)) {
11530           case 'x' : case 'w' : siz[k] = img._width; ++s; break;
11531           case 'y' : case 'h' : siz[k] = img._height; ++s; break;
11532           case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
11533           case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
11534           default :
11535             throw CImgArgumentException(_cimg_instance
11536                                         "assign(): Invalid character '%c' detected in specified dimension string '%s'.",
11537                                         cimg_instance,
11538                                         *s,dimensions);
11539           }
11540         }
11541       }
11542       return assign(siz[0],siz[1],siz[2],siz[3]);
11543     }
11544 
11545     //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace.
11546     /**
11547        In-place version of the constructor CImg(const CImg<t>&,const char*,T).
11548     **/
11549     template<typename t>
11550     CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T& value) {
11551       return assign(img,dimensions).fill(value);
11552     }
11553 
11554     //! Construct image from a display window \inplace.
11555     /**
11556        In-place version of the constructor CImg(const CImgDisplay&).
11557     **/
11558     CImg<T>& assign(const CImgDisplay &disp) {
11559       disp.snapshot(*this);
11560       return *this;
11561     }
11562 
11563     //! Construct empty image \inplace.
11564     /**
11565        Equivalent to assign().
11566        \note
11567        - It has been defined for compatibility with STL naming conventions.
11568     **/
11569     CImg<T>& clear() {
11570       return assign();
11571     }
11572 
11573     //! Transfer content of an image instance into another one.
11574     /**
11575        Transfer the dimensions and the pixel buffer content of an image instance into another one,
11576        and replace instance by an empty image. It avoids the copy of the pixel buffer
11577        when possible.
11578        \param img Destination image.
11579        \note
11580        - Pixel types \c T and \c t of source and destination images can be different, though the process is
11581          designed to be instantaneous when \c T and \c t are the same.
11582        \par Example
11583        \code
11584        CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'.
11585                    dest(16,16);        // Construct a 16x16x1x1 (scalar) image.
11586        src.move_to(dest);              // Now, 'src' is empty and 'dest' is the 256x256x1x3 image.
11587        \endcode
11588     **/
11589     template<typename t>
11590     CImg<t>& move_to(CImg<t>& img) {
11591       img.assign(*this);
11592       assign();
11593       return img;
11594     }
11595 
11596     //! Transfer content of an image instance into another one \specialization.
11597     CImg<T>& move_to(CImg<T>& img) {
11598       if (_is_shared || img._is_shared) img.assign(*this);
11599       else swap(img);
11600       assign();
11601       return img;
11602     }
11603 
11604     //! Transfer content of an image instance into a new image in an image list.
11605     /**
11606        Transfer the dimensions and the pixel buffer content of an image instance
11607        into a newly inserted image at position \c pos in specified \c CImgList<t> instance.
11608        \param list Destination list.
11609        \param pos Position of the newly inserted image in the list.
11610        \note
11611        - When optional parameter \c pos is ommited, the image instance is transfered as a new
11612          image at the end of the specified \c list.
11613        - It is convenient to sequentially insert new images into image lists, with no
11614          additional copies of memory buffer.
11615        \par Example
11616        \code
11617        CImgList<float> list;             // Construct an empty image list.
11618        CImg<float> img("reference.jpg"); // Read image from filename.
11619        img.move_to(list);                // Transfer image content as a new item in the list (no buffer copy).
11620        \endcode
11621     **/
11622     template<typename t>
11623     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
11624       const unsigned int npos = pos>list._width?list._width:pos;
11625       move_to(list.insert(1,npos)[npos]);
11626       return list;
11627     }
11628 
11629     //! Swap fields of two image instances.
11630     /**
11631       \param img Image to swap fields with.
11632       \note
11633       - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing
11634         with algorithms requiring two swapping buffers.
11635       \par Example
11636       \code
11637       CImg<float> img1("lena.jpg"),
11638                   img2("milla.jpg");
11639       img1.swap(img2);               // Now, 'img1' is 'milla' and 'img2' is 'lena'.
11640       \endcode
11641     **/
11642     CImg<T>& swap(CImg<T>& img) {
11643       cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum);
11644       cimg::swap(_data,img._data);
11645       cimg::swap(_is_shared,img._is_shared);
11646       return img;
11647     }
11648 
11649     //! Return a reference to an empty image.
11650     /**
11651        \note
11652        This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes,
11653        e.g.
11654        \code
11655        void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty());
11656        \endcode
11657      **/
11658     static CImg<T>& empty() {
11659       static CImg<T> _empty;
11660       return _empty.assign();
11661     }
11662 
11663     //! Return a reference to an empty image \const.
11664     static const CImg<T>& const_empty() {
11665       static const CImg<T> _empty;
11666       return _empty;
11667     }
11668 
11669     //@}
11670     //------------------------------------------
11671     //
11672     //! \name Overloaded Operators
11673     //@{
11674     //------------------------------------------
11675 
11676     //! Access to a pixel value.
11677     /**
11678        Return a reference to a located pixel value of the image instance,
11679        being possibly \e const, whether the image instance is \e const or not.
11680        This is the standard method to get/set pixel values in \c CImg<T> images.
11681        \param x X-coordinate of the pixel value.
11682        \param y Y-coordinate of the pixel value.
11683        \param z Z-coordinate of the pixel value.
11684        \param c C-coordinate of the pixel value.
11685        \note
11686        - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to
11687          <tt>(width() - 1,height() - 1,depth() - 1,spectrum() - 1)</tt>.
11688        - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the
11689          corresponding dimension is equal to \c 1.
11690          For instance, pixels of a 2d image (depth() equal to \c 1) can be accessed by <tt>img(x,y,c)</tt> instead of
11691          <tt>img(x,y,0,c)</tt>.
11692        \warning
11693        - There is \e no boundary checking done in this operator, to make it as fast as possible.
11694          You \e must take care of out-of-bounds access by yourself, if necessary.
11695          For debuging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary
11696          checking operations in this operator. In that case, warning messages will be printed on the error output
11697          when accessing out-of-bounds pixels.
11698        \par Example
11699        \code
11700        CImg<float> img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0'.
11701        const float
11702           valR = img(10,10,0,0), // Read red value at coordinates (10,10).
11703           valG = img(10,10,0,1), // Read green value at coordinates (10,10)
11704           valB = img(10,10,2),   // Read blue value at coordinates (10,10) (Z-coordinate can be omitted).
11705           avg = (valR + valG + valB)/3; // Compute average pixel value.
11706        img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value.
11707        \endcode
11708     **/
11709 #if cimg_verbosity>=3
11710     T& operator()(const unsigned int x, const unsigned int y=0,
11711                   const unsigned int z=0, const unsigned int c=0) {
11712       const ulongT off = (ulongT)offset(x,y,z,c);
11713       if (!_data || off>=size()) {
11714         cimg::warn(_cimg_instance
11715                    "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].",
11716                    cimg_instance,
11717                    (int)x,(int)y,(int)z,(int)c,off);
11718         return *_data;
11719       }
11720       else return _data[off];
11721     }
11722 
11723     //! Access to a pixel value \const.
11724     const T& operator()(const unsigned int x, const unsigned int y=0,
11725                         const unsigned int z=0, const unsigned int c=0) const {
11726       return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
11727     }
11728 
11729     //! Access to a pixel value.
11730     /**
11731        \param x X-coordinate of the pixel value.
11732        \param y Y-coordinate of the pixel value.
11733        \param z Z-coordinate of the pixel value.
11734        \param c C-coordinate of the pixel value.
11735        \param wh Precomputed offset, must be equal to <tt>width()*\ref height()</tt>.
11736        \param whd Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>.
11737        \note
11738        - Similar to (but faster than) operator()().
11739          It uses precomputed offsets to optimize memory access. You may use it to optimize
11740          the reading/writing of several pixel values in the same image (e.g. in a loop).
11741      **/
11742     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
11743                   const ulongT wh, const ulongT whd=0) {
11744       cimg::unused(wh,whd);
11745       return (*this)(x,y,z,c);
11746     }
11747 
11748     //! Access to a pixel value \const.
11749     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
11750                         const ulongT wh, const ulongT whd=0) const {
11751       cimg::unused(wh,whd);
11752       return (*this)(x,y,z,c);
11753     }
11754 #else
11755     T& operator()(const unsigned int x) {
11756       return _data[x];
11757     }
11758 
11759     const T& operator()(const unsigned int x) const {
11760       return _data[x];
11761     }
11762 
11763     T& operator()(const unsigned int x, const unsigned int y) {
11764       return _data[x + y*_width];
11765     }
11766 
11767     const T& operator()(const unsigned int x, const unsigned int y) const {
11768       return _data[x + y*_width];
11769     }
11770 
11771     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
11772       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
11773    }
11774 
11775     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
11776       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
11777     }
11778 
11779     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
11780       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
11781     }
11782 
11783     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
11784       return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
11785     }
11786 
11787     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
11788                   const ulongT wh) {
11789       return _data[x + y*_width + z*wh];
11790     }
11791 
11792     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
11793                         const ulongT wh) const {
11794       return _data[x + y*_width + z*wh];
11795     }
11796 
11797     T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
11798                   const ulongT wh, const ulongT whd) {
11799       return _data[x + y*_width + z*wh + c*whd];
11800     }
11801 
11802     const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
11803                         const ulongT wh, const ulongT whd) const {
11804       return _data[x + y*_width + z*wh + c*whd];
11805     }
11806 #endif
11807 
11808     //! Implicitely cast an image into a \c T*.
11809     /**
11810        Implicitely cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance
11811        is \e const or not. The returned pointer points on the first value of the image pixel buffer.
11812        \note
11813        - It simply returns the pointer data() to the pixel buffer.
11814        - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g.
11815        \code
11816        CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image.
11817        if (img1) {                      // Test succeeds, 'img1' is not an empty image.
11818          if (!img2) {                   // Test succeeds, 'img2' is an empty image.
11819            std::printf("'img1' is not empty, 'img2' is empty.");
11820          }
11821        }
11822        \endcode
11823        - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g.
11824        \code
11825        CImg<float> img(100,100);
11826        const float value = img[99]; // Access to value of the last pixel on the first row.
11827        img[510] = 255;              // Set pixel value at (10,5).
11828        \endcode
11829     **/
11830     operator T*() {
11831       return _data;
11832     }
11833 
11834     //! Implicitely cast an image into a \c T* \const.
11835     operator const T*() const {
11836       return _data;
11837     }
11838 
11839     //! Assign a value to all image pixels.
11840     /**
11841        Assign specified \c value to each pixel value of the image instance.
11842        \param value Value that will be assigned to image pixels.
11843        \note
11844        - The image size is never modified.
11845        - The \c value may be casted to pixel type \c T if necessary.
11846        \par Example
11847        \code
11848        CImg<char> img(100,100); // Declare image (with garbage values).
11849        img = 0;                 // Set all pixel values to '0'.
11850        img = 1.2;               // Set all pixel values to '1' (cast of '1.2' as a 'char').
11851        \endcode
11852     **/
11853     CImg<T>& operator=(const T& value) {
11854       return fill(value);
11855     }
11856 
11857     //! Assign pixels values from a specified expression.
11858     /**
11859        Initialize all pixel values from the specified string \c expression.
11860        \param expression Value string describing the way pixel values are set.
11861        \note
11862        - String parameter \c expression may describe different things:
11863          - If \c expression is a list of values (as in \c "1,2,3,8,3,2"), or a formula (as in \c "(x*y)%255"),
11864            the pixel values are set from specified \c expression and the image size is not modified.
11865          - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and
11866            replace the image instance. The image size is modified if necessary.
11867        \par Example
11868        \code
11869        CImg<float> img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with unitialized values.
11870        img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence.
11871        img2 = "10*((x*y)%25)";                       // Set pixel values of 'img2' from a formula.
11872        img3 = "reference.jpg";                       // Set pixel values of 'img3' from a file (image size is modified).
11873        (img1,img2,img3).display();
11874        \endcode
11875        \image html ref_operator_eq.jpg
11876     **/
11877     CImg<T>& operator=(const char *const expression) {
11878       const unsigned int omode = cimg::exception_mode();
11879       cimg::exception_mode(0);
11880       try {
11881         _fill(expression,true,true,0,0,"operator=",0);
11882       } catch (CImgException&) {
11883         cimg::exception_mode(omode);
11884         load(expression);
11885       }
11886       cimg::exception_mode(omode);
11887       return *this;
11888     }
11889 
11890     //! Copy an image into the current image instance.
11891     /**
11892        Similar to the in-place copy constructor assign(const CImg<t>&).
11893     **/
11894     template<typename t>
11895     CImg<T>& operator=(const CImg<t>& img) {
11896       return assign(img);
11897     }
11898 
11899     //! Copy an image into the current image instance \specialization.
11900     CImg<T>& operator=(const CImg<T>& img) {
11901       return assign(img);
11902     }
11903 
11904     //! Copy the content of a display window to the current image instance.
11905     /**
11906        Similar to assign(const CImgDisplay&).
11907     **/
11908     CImg<T>& operator=(const CImgDisplay& disp) {
11909       disp.snapshot(*this);
11910       return *this;
11911     }
11912 
11913     //! In-place addition operator.
11914     /**
11915        Add specified \c value to all pixels of an image instance.
11916        \param value Value to add.
11917        \note
11918        - Resulting pixel values are casted to fit the pixel type \c T.
11919          For instance, adding \c 0.2 to a \c CImg<char> is possible but does nothing indeed.
11920        - Overflow values are treated as with standard C++ numeric types. For instance,
11921        \code
11922        CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'.
11923        img+=1;                                   // Add '1' to each pixels -> Overflow.
11924        // here all pixels of image 'img' are equal to '0'.
11925        \endcode
11926        - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double,
11927          and use cut() after addition.
11928        \par Example
11929        \code
11930        CImg<unsigned char> img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]).
11931        CImg<float> img2(img1); // Construct a float-valued copy of 'img1'.
11932        img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats.
11933        img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint.
11934        img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1'.
11935        const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way.
11936        (img1,img2,img3).display();
11937        \endcode
11938        \image html ref_operator_plus.jpg
11939      **/
11940     template<typename t>
11941     CImg<T>& operator+=(const t value) {
11942       if (is_empty()) return *this;
11943       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
11944       cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd + value);
11945       return *this;
11946     }
11947 
11948     //! In-place addition operator.
11949     /**
11950        Add values to image pixels, according to the specified string \c expression.
11951        \param expression Value string describing the way pixel values are added.
11952        \note
11953        - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance,
11954          instead of assigning them.
11955     **/
11956     CImg<T>& operator+=(const char *const expression) {
11957       return *this+=(+*this)._fill(expression,true,true,0,0,"operator+=",this);
11958     }
11959 
11960     //! In-place addition operator.
11961     /**
11962        Add values to image pixels, according to the values of the input image \c img.
11963        \param img Input image to add.
11964        \note
11965        - The size of the image instance is never modified.
11966        - It is not mandatory that input image \c img has the same size as the image instance.
11967          If less values are available in \c img, then the values are added periodically. For instance, adding one
11968          WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3)
11969          means each color channel will be incremented with the same values at the same locations.
11970        \par Example
11971        \code
11972        CImg<float> img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3)
11973        // Construct a scalar shading (img2.spectrum()==1).
11974        const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2");
11975        img1+=img2; // Add shading to each channel of 'img1'.
11976        img1.cut(0,255); // Prevent [0,255] overflow.
11977        (img2,img1).display();
11978        \endcode
11979        \image html ref_operator_plus1.jpg
11980     **/
11981     template<typename t>
11982     CImg<T>& operator+=(const CImg<t>& img) {
11983       const ulongT siz = size(), isiz = img.size();
11984       if (siz && isiz) {
11985         if (is_overlapped(img)) return *this+=+img;
11986         T *ptrd = _data, *const ptre = _data + siz;
11987         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
11988           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
11989             *ptrd = (T)(*ptrd + *(ptrs++));
11990         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
11991       }
11992       return *this;
11993     }
11994 
11995     //! In-place increment operator (prefix).
11996     /**
11997        Add \c 1 to all image pixels, and return a reference to the current incremented image instance.
11998        \note
11999        - Writing \c ++img is equivalent to \c img+=1.
12000      **/
12001     CImg<T>& operator++() {
12002       if (is_empty()) return *this;
12003       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
12004       cimg_rof(*this,ptrd,T) ++*ptrd;
12005       return *this;
12006     }
12007 
12008     //! In-place increment operator (postfix).
12009     /**
12010        Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance.
12011        \note
12012        - Use the prefixed version operator++() if you don't need a copy of the initial
12013          (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage.
12014      **/
12015     CImg<T> operator++(int) {
12016       const CImg<T> copy(*this,false);
12017       ++*this;
12018       return copy;
12019     }
12020 
12021     //! Return a non-shared copy of the image instance.
12022     /**
12023        \note
12024        - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T.
12025          Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image,
12026          and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no
12027          information about the shared state of the input image.
12028        - Writing \c (+img) is equivalent to \c CImg<T>(img,false).
12029     **/
12030     CImg<T> operator+() const {
12031       return CImg<T>(*this,false);
12032     }
12033 
12034     //! Addition operator.
12035     /**
12036        Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place.
12037        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12038      **/
12039     template<typename t>
12040     CImg<_cimg_Tt> operator+(const t value) const {
12041       return CImg<_cimg_Tt>(*this,false)+=value;
12042     }
12043 
12044     //! Addition operator.
12045     /**
12046        Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place.
12047        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12048      **/
12049     CImg<Tfloat> operator+(const char *const expression) const {
12050       return CImg<Tfloat>(*this,false)+=expression;
12051     }
12052 
12053     //! Addition operator.
12054     /**
12055        Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12056        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12057      **/
12058     template<typename t>
12059     CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
12060       return CImg<_cimg_Tt>(*this,false)+=img;
12061     }
12062 
12063     //! In-place substraction operator.
12064     /**
12065        Similar to operator+=(const t), except that it performs a substraction instead of an addition.
12066      **/
12067     template<typename t>
12068     CImg<T>& operator-=(const t value) {
12069       if (is_empty()) return *this;
12070       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
12071       cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd - value);
12072       return *this;
12073     }
12074 
12075     //! In-place substraction operator.
12076     /**
12077        Similar to operator+=(const char*), except that it performs a substraction instead of an addition.
12078      **/
12079     CImg<T>& operator-=(const char *const expression) {
12080       return *this-=(+*this)._fill(expression,true,true,0,0,"operator-=",this);
12081     }
12082 
12083     //! In-place substraction operator.
12084     /**
12085        Similar to operator+=(const CImg<t>&), except that it performs a substraction instead of an addition.
12086      **/
12087     template<typename t>
12088     CImg<T>& operator-=(const CImg<t>& img) {
12089       const ulongT siz = size(), isiz = img.size();
12090       if (siz && isiz) {
12091         if (is_overlapped(img)) return *this-=+img;
12092         T *ptrd = _data, *const ptre = _data + siz;
12093         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12094           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12095             *ptrd = (T)(*ptrd - *(ptrs++));
12096         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
12097       }
12098       return *this;
12099     }
12100 
12101     //! In-place decrement operator (prefix).
12102     /**
12103        Similar to operator++(), except that it performs a decrement instead of an increment.
12104     **/
12105     CImg<T>& operator--() {
12106       if (is_empty()) return *this;
12107       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=524288))
12108       cimg_rof(*this,ptrd,T) *ptrd = *ptrd - (T)1;
12109       return *this;
12110     }
12111 
12112     //! In-place decrement operator (postfix).
12113     /**
12114        Similar to operator++(int), except that it performs a decrement instead of an increment.
12115     **/
12116     CImg<T> operator--(int) {
12117       const CImg<T> copy(*this,false);
12118       --*this;
12119       return copy;
12120     }
12121 
12122     //! Replace each pixel by its opposite value.
12123     /**
12124        \note
12125        - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types.
12126          For instance, the \c unsigned \c char opposite of \c 1 is \c 255.
12127        \par Example
12128        \code
12129        const CImg<unsigned char>
12130          img1("reference.jpg"),   // Load a RGB color image.
12131          img2 = -img1;            // Compute its opposite (in 'unsigned char').
12132        (img1,img2).display();
12133        \endcode
12134        \image html ref_operator_minus.jpg
12135      **/
12136     CImg<T> operator-() const {
12137       return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
12138     }
12139 
12140     //! Substraction operator.
12141     /**
12142        Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place.
12143        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12144     **/
12145     template<typename t>
12146     CImg<_cimg_Tt> operator-(const t value) const {
12147       return CImg<_cimg_Tt>(*this,false)-=value;
12148     }
12149 
12150     //! Substraction operator.
12151     /**
12152        Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place.
12153        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12154     **/
12155     CImg<Tfloat> operator-(const char *const expression) const {
12156       return CImg<Tfloat>(*this,false)-=expression;
12157     }
12158 
12159     //! Substraction operator.
12160     /**
12161        Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12162        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12163     **/
12164     template<typename t>
12165     CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
12166       return CImg<_cimg_Tt>(*this,false)-=img;
12167     }
12168 
12169     //! In-place multiplication operator.
12170     /**
12171        Similar to operator+=(const t), except that it performs a multiplication instead of an addition.
12172      **/
12173     template<typename t>
12174     CImg<T>& operator*=(const t value) {
12175       if (is_empty()) return *this;
12176       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144))
12177       cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd * value);
12178       return *this;
12179     }
12180 
12181     //! In-place multiplication operator.
12182     /**
12183        Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
12184      **/
12185     CImg<T>& operator*=(const char *const expression) {
12186       return mul((+*this)._fill(expression,true,true,0,0,"operator*=",this));
12187     }
12188 
12189     //! In-place multiplication operator.
12190     /**
12191        Replace the image instance by the matrix multiplication between the image instance and the specified matrix
12192        \c img.
12193        \param img Second operand of the matrix multiplication.
12194        \note
12195        - It does \e not compute a pointwise multiplication between two images. For this purpose, use
12196          mul(const CImg<t>&) instead.
12197        - The size of the image instance can be modified by this operator.
12198        \par Example
12199        \code
12200        CImg<float> A(2,2,1,1, 1,2,3,4);   // Construct 2x2 matrix A = [1,2;3,4].
12201        const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2].
12202        A*=X;                              // Assign matrix multiplication A*X to 'A'.
12203        // 'A' is now a 1x2 vector whose values are [5;11].
12204        \endcode
12205     **/
12206     template<typename t>
12207     CImg<T>& operator*=(const CImg<t>& img) {
12208       return ((*this)*img).move_to(*this);
12209     }
12210 
12211     //! Multiplication operator.
12212     /**
12213        Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place.
12214        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12215     **/
12216     template<typename t>
12217     CImg<_cimg_Tt> operator*(const t value) const {
12218       return CImg<_cimg_Tt>(*this,false)*=value;
12219     }
12220 
12221     //! Multiplication operator.
12222     /**
12223        Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place.
12224        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12225     **/
12226     CImg<Tfloat> operator*(const char *const expression) const {
12227       return CImg<Tfloat>(*this,false)*=expression;
12228     }
12229 
12230     //! Multiplication operator.
12231     /**
12232        Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12233        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12234     **/
12235     template<typename t>
12236     CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
12237       if (_width!=img._height || _depth!=1 || _spectrum!=1)
12238         throw CImgArgumentException(_cimg_instance
12239                                     "operator*(): Invalid multiplication of instance by specified "
12240                                     "matrix (%u,%u,%u,%u,%p)",
12241                                     cimg_instance,
12242                                     img._width,img._height,img._depth,img._spectrum,img._data);
12243       CImg<_cimg_Tt> res(img._width,_height);
12244 #ifdef cimg_use_openmp
12245       cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(size()>1024 && img.size()>1024))
12246       cimg_forXY(res,i,j) {
12247         _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); res(i,j) = (_cimg_Tt)value;
12248       }
12249 #else
12250       _cimg_Tt *ptrd = res._data;
12251       cimg_forXY(res,i,j) {
12252         _cimg_Ttdouble value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = (_cimg_Tt)value;
12253       }
12254 #endif
12255       return res;
12256     }
12257 
12258     //! In-place division operator.
12259     /**
12260        Similar to operator+=(const t), except that it performs a division instead of an addition.
12261      **/
12262     template<typename t>
12263     CImg<T>& operator/=(const t value) {
12264       if (is_empty()) return *this;
12265       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
12266       cimg_rof(*this,ptrd,T) *ptrd = (T)(*ptrd / value);
12267       return *this;
12268     }
12269 
12270     //! In-place division operator.
12271     /**
12272        Similar to operator+=(const char*), except that it performs a division instead of an addition.
12273      **/
12274     CImg<T>& operator/=(const char *const expression) {
12275       return div((+*this)._fill(expression,true,true,0,0,"operator/=",this));
12276     }
12277 
12278     //! In-place division operator.
12279     /**
12280        Replace the image instance by the (right) matrix division between the image instance and the specified
12281        matrix \c img.
12282        \param img Second operand of the matrix division.
12283        \note
12284        - It does \e not compute a pointwise division between two images. For this purpose, use
12285          div(const CImg<t>&) instead.
12286        - It returns the matrix operation \c A*inverse(img).
12287        - The size of the image instance can be modified by this operator.
12288      **/
12289     template<typename t>
12290     CImg<T>& operator/=(const CImg<t>& img) {
12291       return (*this*img.get_invert()).move_to(*this);
12292     }
12293 
12294     //! Division operator.
12295     /**
12296        Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place.
12297        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12298     **/
12299     template<typename t>
12300     CImg<_cimg_Tt> operator/(const t value) const {
12301       return CImg<_cimg_Tt>(*this,false)/=value;
12302     }
12303 
12304     //! Division operator.
12305     /**
12306        Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place.
12307        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12308     **/
12309     CImg<Tfloat> operator/(const char *const expression) const {
12310       return CImg<Tfloat>(*this,false)/=expression;
12311     }
12312 
12313     //! Division operator.
12314     /**
12315        Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12316        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12317     **/
12318     template<typename t>
12319     CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
12320       return (*this)*img.get_invert();
12321     }
12322 
12323     //! In-place modulo operator.
12324     /**
12325        Similar to operator+=(const t), except that it performs a modulo operation instead of an addition.
12326     **/
12327     template<typename t>
12328     CImg<T>& operator%=(const t value) {
12329       if (is_empty()) return *this;
12330       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=16384))
12331       cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value);
12332       return *this;
12333     }
12334 
12335     //! In-place modulo operator.
12336     /**
12337        Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
12338     **/
12339     CImg<T>& operator%=(const char *const expression) {
12340       return *this%=(+*this)._fill(expression,true,true,0,0,"operator%=",this);
12341     }
12342 
12343     //! In-place modulo operator.
12344     /**
12345        Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition.
12346     **/
12347     template<typename t>
12348     CImg<T>& operator%=(const CImg<t>& img) {
12349       const ulongT siz = size(), isiz = img.size();
12350       if (siz && isiz) {
12351         if (is_overlapped(img)) return *this%=+img;
12352         T *ptrd = _data, *const ptre = _data + siz;
12353         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12354           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12355             *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
12356         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
12357       }
12358       return *this;
12359     }
12360 
12361     //! Modulo operator.
12362     /**
12363        Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place.
12364        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12365     **/
12366     template<typename t>
12367     CImg<_cimg_Tt> operator%(const t value) const {
12368       return CImg<_cimg_Tt>(*this,false)%=value;
12369     }
12370 
12371     //! Modulo operator.
12372     /**
12373        Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place.
12374        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12375     **/
12376     CImg<Tfloat> operator%(const char *const expression) const {
12377       return CImg<Tfloat>(*this,false)%=expression;
12378     }
12379 
12380     //! Modulo operator.
12381     /**
12382        Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12383        The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
12384     **/
12385     template<typename t>
12386     CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
12387       return CImg<_cimg_Tt>(*this,false)%=img;
12388     }
12389 
12390     //! In-place bitwise AND operator.
12391     /**
12392        Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition.
12393     **/
12394     template<typename t>
12395     CImg<T>& operator&=(const t value) {
12396       if (is_empty()) return *this;
12397       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
12398       cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd & (ulongT)value);
12399       return *this;
12400     }
12401 
12402     //! In-place bitwise AND operator.
12403     /**
12404        Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
12405     **/
12406     CImg<T>& operator&=(const char *const expression) {
12407       return *this&=(+*this)._fill(expression,true,true,0,0,"operator&=",this);
12408     }
12409 
12410     //! In-place bitwise AND operator.
12411     /**
12412        Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition.
12413     **/
12414     template<typename t>
12415     CImg<T>& operator&=(const CImg<t>& img) {
12416       const ulongT siz = size(), isiz = img.size();
12417       if (siz && isiz) {
12418         if (is_overlapped(img)) return *this&=+img;
12419         T *ptrd = _data, *const ptre = _data + siz;
12420         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12421           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12422             *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
12423         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd & (ulongT)*(ptrs++));
12424       }
12425       return *this;
12426     }
12427 
12428     //! Bitwise AND operator.
12429     /**
12430        Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place.
12431        The pixel type of the returned image is \c T.
12432     **/
12433     template<typename t>
12434     CImg<T> operator&(const t value) const {
12435       return (+*this)&=value;
12436     }
12437 
12438     //! Bitwise AND operator.
12439     /**
12440        Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place.
12441        The pixel type of the returned image is \c T.
12442     **/
12443     CImg<T> operator&(const char *const expression) const {
12444       return (+*this)&=expression;
12445     }
12446 
12447     //! Bitwise AND operator.
12448     /**
12449        Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12450        The pixel type of the returned image is \c T.
12451     **/
12452     template<typename t>
12453     CImg<T> operator&(const CImg<t>& img) const {
12454       return (+*this)&=img;
12455     }
12456 
12457     //! In-place bitwise OR operator.
12458     /**
12459        Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition.
12460     **/
12461     template<typename t>
12462     CImg<T>& operator|=(const t value) {
12463       if (is_empty()) return *this;
12464       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
12465       cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd | (ulongT)value);
12466       return *this;
12467     }
12468 
12469     //! In-place bitwise OR operator.
12470     /**
12471        Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
12472     **/
12473     CImg<T>& operator|=(const char *const expression) {
12474       return *this|=(+*this)._fill(expression,true,true,0,0,"operator|=",this);
12475     }
12476 
12477     //! In-place bitwise OR operator.
12478     /**
12479        Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition.
12480     **/
12481     template<typename t>
12482     CImg<T>& operator|=(const CImg<t>& img) {
12483       const ulongT siz = size(), isiz = img.size();
12484       if (siz && isiz) {
12485         if (is_overlapped(img)) return *this|=+img;
12486         T *ptrd = _data, *const ptre = _data + siz;
12487         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12488           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12489             *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
12490         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd | (ulongT)*(ptrs++));
12491       }
12492       return *this;
12493     }
12494 
12495     //! Bitwise OR operator.
12496     /**
12497        Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place.
12498        The pixel type of the returned image is \c T.
12499     **/
12500     template<typename t>
12501     CImg<T> operator|(const t value) const {
12502       return (+*this)|=value;
12503     }
12504 
12505     //! Bitwise OR operator.
12506     /**
12507        Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place.
12508        The pixel type of the returned image is \c T.
12509     **/
12510     CImg<T> operator|(const char *const expression) const {
12511       return (+*this)|=expression;
12512     }
12513 
12514     //! Bitwise OR operator.
12515     /**
12516        Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12517        The pixel type of the returned image is \c T.
12518     **/
12519     template<typename t>
12520     CImg<T> operator|(const CImg<t>& img) const {
12521       return (+*this)|=img;
12522     }
12523 
12524     //! In-place bitwise XOR operator.
12525     /**
12526        Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition.
12527        \warning
12528        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead.
12529     **/
12530     template<typename t>
12531     CImg<T>& operator^=(const t value) {
12532       if (is_empty()) return *this;
12533       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
12534       cimg_rof(*this,ptrd,T) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)value);
12535       return *this;
12536     }
12537 
12538     //! In-place bitwise XOR operator.
12539     /**
12540        Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition.
12541        \warning
12542        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
12543     **/
12544     CImg<T>& operator^=(const char *const expression) {
12545       return *this^=(+*this)._fill(expression,true,true,0,0,"operator^=",this);
12546     }
12547 
12548     //! In-place bitwise XOR operator.
12549     /**
12550        Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition.
12551        \warning
12552        - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead.
12553     **/
12554     template<typename t>
12555     CImg<T>& operator^=(const CImg<t>& img) {
12556       const ulongT siz = size(), isiz = img.size();
12557       if (siz && isiz) {
12558         if (is_overlapped(img)) return *this^=+img;
12559         T *ptrd = _data, *const ptre = _data + siz;
12560         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12561           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12562             *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
12563         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((ulongT)*ptrd ^ (ulongT)*(ptrs++));
12564       }
12565       return *this;
12566     }
12567 
12568     //! Bitwise XOR operator.
12569     /**
12570        Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place.
12571        The pixel type of the returned image is \c T.
12572     **/
12573     template<typename t>
12574     CImg<T> operator^(const t value) const {
12575       return (+*this)^=value;
12576     }
12577 
12578     //! Bitwise XOR operator.
12579     /**
12580        Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place.
12581        The pixel type of the returned image is \c T.
12582     **/
12583     CImg<T> operator^(const char *const expression) const {
12584       return (+*this)^=expression;
12585     }
12586 
12587     //! Bitwise XOR operator.
12588     /**
12589        Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
12590        The pixel type of the returned image is \c T.
12591     **/
12592     template<typename t>
12593     CImg<T> operator^(const CImg<t>& img) const {
12594       return (+*this)^=img;
12595     }
12596 
12597     //! In-place bitwise left shift operator.
12598     /**
12599        Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition.
12600     **/
12601     template<typename t>
12602     CImg<T>& operator<<=(const t value) {
12603       if (is_empty()) return *this;
12604       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
12605       cimg_rof(*this,ptrd,T) *ptrd = (T)(((longT)*ptrd) << (int)value);
12606       return *this;
12607     }
12608 
12609     //! In-place bitwise left shift operator.
12610     /**
12611        Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
12612     **/
12613     CImg<T>& operator<<=(const char *const expression) {
12614       return *this<<=(+*this)._fill(expression,true,true,0,0,"operator<<=",this);
12615     }
12616 
12617     //! In-place bitwise left shift operator.
12618     /**
12619        Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition.
12620     **/
12621     template<typename t>
12622     CImg<T>& operator<<=(const CImg<t>& img) {
12623       const ulongT siz = size(), isiz = img.size();
12624       if (siz && isiz) {
12625         if (is_overlapped(img)) return *this^=+img;
12626         T *ptrd = _data, *const ptre = _data + siz;
12627         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12628           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12629             *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
12630         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
12631       }
12632       return *this;
12633     }
12634 
12635     //! Bitwise left shift operator.
12636     /**
12637        Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place.
12638        The pixel type of the returned image is \c T.
12639     **/
12640     template<typename t>
12641     CImg<T> operator<<(const t value) const {
12642       return (+*this)<<=value;
12643     }
12644 
12645     //! Bitwise left shift operator.
12646     /**
12647        Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place.
12648        The pixel type of the returned image is \c T.
12649     **/
12650     CImg<T> operator<<(const char *const expression) const {
12651       return (+*this)<<=expression;
12652     }
12653 
12654     //! Bitwise left shift operator.
12655     /**
12656        Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of
12657        operating in-place.
12658        The pixel type of the returned image is \c T.
12659     **/
12660     template<typename t>
12661     CImg<T> operator<<(const CImg<t>& img) const {
12662       return (+*this)<<=img;
12663     }
12664 
12665     //! In-place bitwise right shift operator.
12666     /**
12667        Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition.
12668     **/
12669     template<typename t>
12670     CImg<T>& operator>>=(const t value) {
12671       if (is_empty()) return *this;
12672       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
12673       cimg_rof(*this,ptrd,T) *ptrd = (T)(((longT)*ptrd) >> (int)value);
12674       return *this;
12675     }
12676 
12677     //! In-place bitwise right shift operator.
12678     /**
12679        Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
12680     **/
12681     CImg<T>& operator>>=(const char *const expression) {
12682       return *this>>=(+*this)._fill(expression,true,true,0,0,"operator>>=",this);
12683     }
12684 
12685     //! In-place bitwise right shift operator.
12686     /**
12687        Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition.
12688     **/
12689     template<typename t>
12690     CImg<T>& operator>>=(const CImg<t>& img) {
12691       const ulongT siz = size(), isiz = img.size();
12692       if (siz && isiz) {
12693         if (is_overlapped(img)) return *this^=+img;
12694         T *ptrd = _data, *const ptre = _data + siz;
12695         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
12696           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
12697             *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
12698         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
12699       }
12700       return *this;
12701     }
12702 
12703     //! Bitwise right shift operator.
12704     /**
12705        Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place.
12706        The pixel type of the returned image is \c T.
12707     **/
12708     template<typename t>
12709     CImg<T> operator>>(const t value) const {
12710       return (+*this)>>=value;
12711     }
12712 
12713     //! Bitwise right shift operator.
12714     /**
12715        Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place.
12716        The pixel type of the returned image is \c T.
12717     **/
12718     CImg<T> operator>>(const char *const expression) const {
12719       return (+*this)>>=expression;
12720     }
12721 
12722     //! Bitwise right shift operator.
12723     /**
12724        Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of
12725        operating in-place.
12726        The pixel type of the returned image is \c T.
12727     **/
12728     template<typename t>
12729     CImg<T> operator>>(const CImg<t>& img) const {
12730       return (+*this)>>=img;
12731     }
12732 
12733     //! Bitwise inversion operator.
12734     /**
12735        Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value.
12736     **/
12737     CImg<T> operator~() const {
12738       CImg<T> res(_width,_height,_depth,_spectrum);
12739       const T *ptrs = _data;
12740       cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; }
12741       return res;
12742     }
12743 
12744     //! Test if all pixels of an image have the same value.
12745     /**
12746        Return \c true is all pixels of the image instance are equal to the specified \c value.
12747        \param value Reference value to compare with.
12748     **/
12749     template<typename t>
12750     bool operator==(const t value) const {
12751       if (is_empty()) return false;
12752       typedef _cimg_Tt Tt;
12753       bool is_equal = true;
12754       for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
12755       return is_equal;
12756     }
12757 
12758     //! Test if all pixel values of an image follow a specified expression.
12759     /**
12760        Return \c true is all pixels of the image instance are equal to the specified \c expression.
12761        \param expression Value string describing the way pixel values are compared.
12762     **/
12763     bool operator==(const char *const expression) const {
12764       return *this==(+*this)._fill(expression,true,true,0,0,"operator==",this);
12765     }
12766 
12767     //! Test if two images have the same size and values.
12768     /**
12769        Return \c true if the image instance and the input image \c img have the same dimensions and pixel values,
12770        and \c false otherwise.
12771        \param img Input image to compare with.
12772        \note
12773        - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==()
12774          to return \c true.
12775          Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different
12776          pixel types \c T and \c t.
12777        \par Example
12778        \code
12779        const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values).
12780        const CImg<char> img2(1,3,1,1, 0,1,2);  // Construct a 1x3 vector [0;1;2] (with 'char' pixel values).
12781        if (img1==img2) {                       // Test succeeds, image dimensions and values are the same.
12782          std::printf("'img1' and 'img2' have same dimensions and values.");
12783        }
12784        \endcode
12785     **/
12786     template<typename t>
12787     bool operator==(const CImg<t>& img) const {
12788       typedef _cimg_Tt Tt;
12789       const ulongT siz = size();
12790       bool is_equal = true;
12791       if (siz!=img.size()) return false;
12792       t *ptrs = img._data + siz;
12793       for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
12794       return is_equal;
12795     }
12796 
12797     //! Test if pixels of an image are all different from a value.
12798     /**
12799        Return \c true is all pixels of the image instance are different than the specified \c value.
12800        \param value Reference value to compare with.
12801     **/
12802     template<typename t>
12803     bool operator!=(const t value) const {
12804       return !((*this)==value);
12805     }
12806 
12807     //! Test if all pixel values of an image are different from a specified expression.
12808     /**
12809        Return \c true is all pixels of the image instance are different to the specified \c expression.
12810        \param expression Value string describing the way pixel values are compared.
12811     **/
12812     bool operator!=(const char *const expression) const {
12813       return !((*this)==expression);
12814     }
12815 
12816     //! Test if two images have different sizes or values.
12817     /**
12818        Return \c true if the image instance and the input image \c img have different dimensions or pixel values,
12819        and \c false otherwise.
12820        \param img Input image to compare with.
12821        \note
12822        - Writing \c img1!=img2 is equivalent to \c !(img1==img2).
12823     **/
12824     template<typename t>
12825     bool operator!=(const CImg<t>& img) const {
12826       return !((*this)==img);
12827     }
12828 
12829     //! Construct an image list from two images.
12830     /**
12831        Return a new list of image (\c CImgList instance) containing exactly two elements:
12832          - A copy of the image instance, at position [\c 0].
12833          - A copy of the specified image \c img, at position [\c 1].
12834 
12835        \param img Input image that will be the second image of the resulting list.
12836        \note
12837        - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow
12838          in practice (see warning below).
12839        - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are
12840          inserted as new non-shared copies in the resulting list.
12841        - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary.
12842        \warning
12843        - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list.
12844          This may become very expensive in terms of speed and used memory. You should avoid using this technique to
12845          build a new CImgList instance from several images, if you are seeking for performance.
12846          Fast insertions of images in an image list are possible with
12847          CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or move_to(CImgList<t>&,unsigned int).
12848        \par Example
12849        \code
12850        const CImg<float>
12851           img1("reference.jpg"),
12852           img2 = img1.get_mirror('x'),
12853           img3 = img2.get_blur(5);
12854        const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2'.
12855        (list,img3).display();                    // Display image list containing copies of 'img1','img2' and 'img3'.
12856        \endcode
12857        \image html ref_operator_comma.jpg
12858     **/
12859     template<typename t>
12860     CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
12861       return CImgList<_cimg_Tt>(*this,img);
12862     }
12863 
12864     //! Construct an image list from image instance and an input image list.
12865     /**
12866        Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements:
12867          - A copy of the image instance, at position [\c 0].
12868          - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()].
12869 
12870        \param list Input image list that will be appended to the image instance.
12871        \note
12872        - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument.
12873     **/
12874     template<typename t>
12875     CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const {
12876       return CImgList<_cimg_Tt>(list,false).insert(*this,0);
12877     }
12878 
12879     //! Split image along specified axis.
12880     /**
12881        Return a new list of images (\c CImgList instance) containing the splitted components
12882        of the instance image along the specified axis.
12883        \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c')
12884        \note
12885        - Similar to get_split(char,int) const, with default second argument.
12886        \par Example
12887        \code
12888        const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image.
12889        const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels.
12890        (img,list).display();
12891        \endcode
12892        \image html ref_operator_less.jpg
12893     **/
12894     CImgList<T> operator<(const char axis) const {
12895       return get_split(axis);
12896     }
12897 
12898     //@}
12899     //-------------------------------------
12900     //
12901     //! \name Instance Characteristics
12902     //@{
12903     //-------------------------------------
12904 
12905     //! Return the type of image pixel values as a C string.
12906     /**
12907        Return a \c char* string containing the usual type name of the image pixel values
12908        (i.e. a stringified version of the template parameter \c T).
12909        \note
12910        - The returned string may contain spaces (as in \c "unsigned char").
12911        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
12912     **/
12913     static const char* pixel_type() {
12914       return cimg::type<T>::string();
12915     }
12916 
12917     //! Return the number of image columns.
12918     /**
12919        Return the image width, i.e. the image dimension along the X-axis.
12920        \note
12921        - The width() of an empty image is equal to \c 0.
12922        - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations.
12923        - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int.
12924          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
12925          \c unsigned \c int variables.
12926          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
12927          <tt>(*this)._width</tt>.
12928     **/
12929     int width() const {
12930       return (int)_width;
12931     }
12932 
12933     //! Return the number of image rows.
12934     /**
12935        Return the image height, i.e. the image dimension along the Y-axis.
12936        \note
12937        - The height() of an empty image is equal to \c 0.
12938        - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int.
12939          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
12940          \c unsigned \c int variables.
12941          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
12942          <tt>(*this)._height</tt>.
12943     **/
12944     int height() const {
12945       return (int)_height;
12946     }
12947 
12948     //! Return the number of image slices.
12949     /**
12950        Return the image depth, i.e. the image dimension along the Z-axis.
12951        \note
12952        - The depth() of an empty image is equal to \c 0.
12953        - depth() is typically equal to \c 1 when considering usual 2d images. When depth()\c > \c 1, the image
12954          is said to be \e volumetric.
12955        - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int.
12956          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
12957          \c unsigned \c int variables.
12958          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
12959          <tt>(*this)._depth</tt>.
12960     **/
12961     int depth() const {
12962       return (int)_depth;
12963     }
12964 
12965     //! Return the number of image channels.
12966     /**
12967        Return the number of image channels, i.e. the image dimension along the C-axis.
12968        \note
12969        - The spectrum() of an empty image is equal to \c 0.
12970        - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3
12971          for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel).
12972          The number of channels of an image instance is not limited. The meaning of the pixel values is not linked
12973          up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image).
12974        - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int.
12975          Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
12976          \c unsigned \c int variables.
12977          Access to the initial \c unsigned \c int variable is possible (though not recommended) by
12978          <tt>(*this)._spectrum</tt>.
12979     **/
12980     int spectrum() const {
12981       return (int)_spectrum;
12982     }
12983 
12984     //! Return the total number of pixel values.
12985     /**
12986        Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>,
12987        i.e. the total number of values of type \c T in the pixel buffer of the image instance.
12988        \note
12989        - The size() of an empty image is equal to \c 0.
12990        - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to
12991          <tt>size()*sizeof(T)</tt>.
12992        \par Example
12993        \code
12994        const CImg<float> img(100,100,1,3);               // Construct new 100x100 color image.
12995        if (img.size()==30000)                            // Test succeeds.
12996          std::printf("Pixel buffer uses %lu bytes",
12997                      img.size()*sizeof(float));
12998        \endcode
12999     **/
13000     ulongT size() const {
13001       return (ulongT)_width*_height*_depth*_spectrum;
13002     }
13003 
13004     //! Return a pointer to the first pixel value.
13005     /**
13006        Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance,
13007        whether the instance is \c const or not.
13008        \note
13009        - The data() of an empty image is equal to \c 0 (null pointer).
13010        - The allocated pixel buffer for the image instance starts from \c data()
13011          and goes to <tt>data()+\ref size() - 1</tt> (included).
13012        - To get the pointer to one particular location of the pixel buffer, use
13013          data(unsigned int,unsigned int,unsigned int,unsigned int) instead.
13014     **/
13015     T* data() {
13016       return _data;
13017     }
13018 
13019     //! Return a pointer to the first pixel value \const.
13020     const T* data() const {
13021       return _data;
13022     }
13023 
13024     //! Return a pointer to a located pixel value.
13025     /**
13026        Return a \c T*, or a \c const \c T* pointer to the value located at (\c x,\c y,\c z,\c c) in the pixel buffer
13027        of the image instance,
13028        whether the instance is \c const or not.
13029        \param x X-coordinate of the pixel value.
13030        \param y Y-coordinate of the pixel value.
13031        \param z Z-coordinate of the pixel value.
13032        \param c C-coordinate of the pixel value.
13033        \note
13034        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c))</tt>. Thus, this method has the same
13035          properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
13036      **/
13037 #if cimg_verbosity>=3
13038     T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
13039       const ulongT off = (ulongT)offset(x,y,z,c);
13040       if (off>=size())
13041         cimg::warn(_cimg_instance
13042                    "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
13043                    cimg_instance,
13044                    x,y,z,c,off);
13045       return _data + off;
13046     }
13047 
13048     //! Return a pointer to a located pixel value \const.
13049     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
13050       return const_cast<CImg<T>*>(this)->data(x,y,z,c);
13051     }
13052 #else
13053     T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
13054       return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
13055     }
13056 
13057     const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
13058       return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
13059     }
13060 #endif
13061 
13062     //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer.
13063     /**
13064        \param x X-coordinate of the pixel value.
13065        \param y Y-coordinate of the pixel value.
13066        \param z Z-coordinate of the pixel value.
13067        \param c C-coordinate of the pixel value.
13068        \note
13069        - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>.
13070          Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
13071        \par Example
13072        \code
13073        const CImg<float> img(100,100,1,3);      // Define a 100x100 RGB-color image.
13074        const long off = img.offset(10,10,0,2);  // Get the offset of the blue value of the pixel located at (10,10).
13075        const float val = img[off];              // Get the blue value of this pixel.
13076        \endcode
13077     **/
13078     longT offset(const int x, const int y=0, const int z=0, const int c=0) const {
13079       return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth;
13080     }
13081 
13082     //! Return a CImg<T>::iterator pointing to the first pixel value.
13083     /**
13084        \note
13085        - Equivalent to data().
13086        - It has been mainly defined for compatibility with STL naming conventions.
13087      **/
13088     iterator begin() {
13089       return _data;
13090     }
13091 
13092     //! Return a CImg<T>::iterator pointing to the first value of the pixel buffer \const.
13093     const_iterator begin() const {
13094       return _data;
13095     }
13096 
13097     //! Return a CImg<T>::iterator pointing next to the last pixel value.
13098     /**
13099        \note
13100        - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>.
13101        - It has been mainly defined for compatibility with STL naming conventions.
13102        \warning
13103        - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer.
13104          Trying to read or write the content of the returned iterator will probably result in a crash.
13105          Use it mainly as a strict upper bound for a CImg<T>::iterator.
13106        \par Example
13107        \code
13108        CImg<float> img(100,100,1,3); // Define a 100x100 RGB color image.
13109        // 'img.end()' used below as an upper bound for the iterator.
13110        for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it)
13111          *it = 0;
13112        \endcode
13113     **/
13114     iterator end() {
13115       return _data + size();
13116     }
13117 
13118     //! Return a CImg<T>::iterator pointing next to the last pixel value \const.
13119     const_iterator end() const {
13120       return _data + size();
13121     }
13122 
13123     //! Return a reference to the first pixel value.
13124     /**
13125        \note
13126        - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>.
13127        - It has been mainly defined for compatibility with STL naming conventions.
13128     **/
13129     T& front() {
13130       return *_data;
13131     }
13132 
13133     //! Return a reference to the first pixel value \const.
13134     const T& front() const {
13135       return *_data;
13136     }
13137 
13138     //! Return a reference to the last pixel value.
13139     /**
13140        \note
13141        - Writing \c img.back() is equivalent to <tt>img[img.size() - 1]</tt>, or
13142          <tt>img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1)</tt>.
13143        - It has been mainly defined for compatibility with STL naming conventions.
13144     **/
13145     T& back() {
13146       return *(_data + size() - 1);
13147     }
13148 
13149     //! Return a reference to the last pixel value \const.
13150     const T& back() const {
13151       return *(_data + size() - 1);
13152     }
13153 
13154     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
13155     /**
13156        Return a reference to the pixel value of the image instance located at a specified \c offset,
13157        or to a specified default value in case of out-of-bounds access.
13158        \param offset Offset to the desired pixel value.
13159        \param out_value Default value returned if \c offset is outside image bounds.
13160        \note
13161        - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset
13162          is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value
13163          is safely returned instead.
13164        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
13165          you are \e not sure about the validity of the specified pixel offset.
13166     **/
13167     T& at(const int offset, const T& out_value) {
13168       return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
13169     }
13170 
13171     //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const.
13172     T at(const int offset, const T& out_value) const {
13173       return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
13174     }
13175 
13176     //! Access to a pixel value at a specified offset, using Neumann boundary conditions.
13177     /**
13178        Return a reference to the pixel value of the image instance located at a specified \c offset,
13179        or to the nearest pixel location in the image instance in case of out-of-bounds access.
13180        \param offset Offset to the desired pixel value.
13181        \note
13182        - Similar to at(int,const T), except that an out-of-bounds access returns the value of the
13183          nearest pixel in the image instance, regarding the specified offset, i.e.
13184          - If \c offset<0, then \c img[0] is returned.
13185          - If \c offset>=img.size(), then \c img[img.size() - 1] is returned.
13186        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
13187          you are \e not sure about the validity of the specified pixel offset.
13188        - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int).
13189      **/
13190     T& at(const int offset) {
13191       if (is_empty())
13192         throw CImgInstanceException(_cimg_instance
13193                                     "at(): Empty instance.",
13194                                     cimg_instance);
13195       return _at(offset);
13196     }
13197 
13198     T& _at(const int offset) {
13199       const unsigned int siz = (unsigned int)size();
13200       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
13201     }
13202 
13203     //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
13204     const T& at(const int offset) const {
13205       if (is_empty())
13206         throw CImgInstanceException(_cimg_instance
13207                                     "at(): Empty instance.",
13208                                     cimg_instance);
13209       return _at(offset);
13210     }
13211 
13212     const T& _at(const int offset) const {
13213       const unsigned int siz = (unsigned int)size();
13214       return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
13215     }
13216 
13217     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
13218     /**
13219        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
13220        or to a specified default value in case of out-of-bounds access along the X-axis.
13221        \param x X-coordinate of the pixel value.
13222        \param y Y-coordinate of the pixel value.
13223        \param z Z-coordinate of the pixel value.
13224        \param c C-coordinate of the pixel value.
13225        \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds.
13226        \note
13227        - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value
13228          \c out_value.
13229        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
13230          you are \e not sure about the validity of the specified pixel coordinates.
13231        \warning
13232        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13233     **/
13234     T& atX(const int x, const int y, const int z, const int c, const T& out_value) {
13235       return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
13236     }
13237 
13238     //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const.
13239     T atX(const int x, const int y, const int z, const int c, const T& out_value) const {
13240       return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
13241     }
13242 
13243     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
13244     /**
13245        Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
13246        or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
13247        \param x X-coordinate of the pixel value.
13248        \param y Y-coordinate of the pixel value.
13249        \param z Z-coordinate of the pixel value.
13250        \param c C-coordinate of the pixel value.
13251        \note
13252        - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the
13253          nearest pixel in the image instance, regarding the specified X-coordinate.
13254        - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
13255          you are \e not sure about the validity of the specified pixel coordinates.
13256        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13257          \c _at(int,int,int,int).
13258        \warning
13259        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13260      **/
13261     T& atX(const int x, const int y=0, const int z=0, const int c=0) {
13262       if (is_empty())
13263         throw CImgInstanceException(_cimg_instance
13264                                     "atX(): Empty instance.",
13265                                     cimg_instance);
13266       return _atX(x,y,z,c);
13267     }
13268 
13269     T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
13270       return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
13271     }
13272 
13273     //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const.
13274     const T& atX(const int x, const int y=0, const int z=0, const int c=0) const {
13275       if (is_empty())
13276         throw CImgInstanceException(_cimg_instance
13277                                     "atX(): Empty instance.",
13278                                     cimg_instance);
13279       return _atX(x,y,z,c);
13280     }
13281 
13282     const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const {
13283       return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
13284     }
13285 
13286     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
13287     /**
13288        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates.
13289     **/
13290     T& atXY(const int x, const int y, const int z, const int c, const T& out_value) {
13291       return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
13292     }
13293 
13294     //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const.
13295     T atXY(const int x, const int y, const int z, const int c, const T& out_value) const {
13296       return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
13297     }
13298 
13299     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
13300     /**
13301        Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates.
13302        \note
13303        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13304          \c _atXY(int,int,int,int).
13305      **/
13306     T& atXY(const int x, const int y, const int z=0, const int c=0) {
13307       if (is_empty())
13308         throw CImgInstanceException(_cimg_instance
13309                                     "atXY(): Empty instance.",
13310                                     cimg_instance);
13311       return _atXY(x,y,z,c);
13312     }
13313 
13314     T& _atXY(const int x, const int y, const int z=0, const int c=0) {
13315       return (*this)(cimg::cut(x,0,width() - 1),
13316                      cimg::cut(y,0,height() - 1),z,c);
13317     }
13318 
13319     //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const.
13320     const T& atXY(const int x, const int y, const int z=0, const int c=0) const {
13321       if (is_empty())
13322         throw CImgInstanceException(_cimg_instance
13323                                     "atXY(): Empty instance.",
13324                                     cimg_instance);
13325       return _atXY(x,y,z,c);
13326     }
13327 
13328     const T& _atXY(const int x, const int y, const int z=0, const int c=0) const {
13329       return (*this)(cimg::cut(x,0,width() - 1),
13330                      cimg::cut(y,0,height() - 1),z,c);
13331     }
13332 
13333     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
13334     /**
13335        Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on
13336        X,Y and Z-coordinates.
13337     **/
13338     T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) {
13339       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
13340         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
13341     }
13342 
13343     //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const.
13344     T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const {
13345       return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
13346     }
13347 
13348     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
13349     /**
13350        Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates.
13351        \note
13352        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13353          \c _atXYZ(int,int,int,int).
13354     **/
13355     T& atXYZ(const int x, const int y, const int z, const int c=0) {
13356       if (is_empty())
13357         throw CImgInstanceException(_cimg_instance
13358                                     "atXYZ(): Empty instance.",
13359                                     cimg_instance);
13360       return _atXYZ(x,y,z,c);
13361     }
13362 
13363     T& _atXYZ(const int x, const int y, const int z, const int c=0) {
13364       return (*this)(cimg::cut(x,0,width() - 1),
13365                      cimg::cut(y,0,height() - 1),
13366                      cimg::cut(z,0,depth() - 1),c);
13367     }
13368 
13369     //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const.
13370     const T& atXYZ(const int x, const int y, const int z, const int c=0) const {
13371       if (is_empty())
13372         throw CImgInstanceException(_cimg_instance
13373                                     "atXYZ(): Empty instance.",
13374                                     cimg_instance);
13375       return _atXYZ(x,y,z,c);
13376     }
13377 
13378     const T& _atXYZ(const int x, const int y, const int z, const int c=0) const {
13379       return (*this)(cimg::cut(x,0,width() - 1),
13380                      cimg::cut(y,0,height() - 1),
13381                      cimg::cut(z,0,depth() - 1),c);
13382     }
13383 
13384     //! Access to a pixel value, using Dirichlet boundary conditions.
13385     /**
13386        Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all
13387        X,Y,Z and C-coordinates.
13388     **/
13389     T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) {
13390       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
13391         (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
13392     }
13393 
13394     //! Access to a pixel value, using Dirichlet boundary conditions \const.
13395     T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const {
13396       return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:
13397         (*this)(x,y,z,c);
13398     }
13399 
13400     //! Access to a pixel value, using Neumann boundary conditions.
13401     /**
13402        Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates.
13403        \note
13404        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13405          \c _atXYZC(int,int,int,int).
13406     **/
13407     T& atXYZC(const int x, const int y, const int z, const int c) {
13408       if (is_empty())
13409         throw CImgInstanceException(_cimg_instance
13410                                     "atXYZC(): Empty instance.",
13411                                     cimg_instance);
13412       return _atXYZC(x,y,z,c);
13413     }
13414 
13415     T& _atXYZC(const int x, const int y, const int z, const int c) {
13416       return (*this)(cimg::cut(x,0,width() - 1),
13417                      cimg::cut(y,0,height() - 1),
13418                      cimg::cut(z,0,depth() - 1),
13419                      cimg::cut(c,0,spectrum() - 1));
13420     }
13421 
13422     //! Access to a pixel value, using Neumann boundary conditions \const.
13423     const T& atXYZC(const int x, const int y, const int z, const int c) const {
13424       if (is_empty())
13425         throw CImgInstanceException(_cimg_instance
13426                                     "atXYZC(): Empty instance.",
13427                                     cimg_instance);
13428       return _atXYZC(x,y,z,c);
13429     }
13430 
13431     const T& _atXYZC(const int x, const int y, const int z, const int c) const {
13432       return (*this)(cimg::cut(x,0,width() - 1),
13433                      cimg::cut(y,0,height() - 1),
13434                      cimg::cut(z,0,depth() - 1),
13435                      cimg::cut(c,0,spectrum() - 1));
13436     }
13437 
13438     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate.
13439     /**
13440        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
13441        or a specified default value in case of out-of-bounds access along the X-axis.
13442        \param fx X-coordinate of the pixel value (float-valued).
13443        \param y Y-coordinate of the pixel value.
13444        \param z Z-coordinate of the pixel value.
13445        \param c C-coordinate of the pixel value.
13446        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
13447        \note
13448        - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by
13449          a linear interpolation along the X-axis, if corresponding coordinates are not integers.
13450        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
13451        \warning
13452        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13453     **/
13454     Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
13455       const int
13456 	x = (int)fx - (fx>=0?0:1), nx = x + 1;
13457       const float
13458         dx = fx - x;
13459       const Tfloat
13460         Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
13461       return Ic + dx*(In - Ic);
13462     }
13463 
13464     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate.
13465     /**
13466        Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
13467        or the value of the nearest pixel location in the image instance in case of out-of-bounds access along
13468        the X-axis.
13469        \param fx X-coordinate of the pixel value (float-valued).
13470        \param y Y-coordinate of the pixel value.
13471        \param z Z-coordinate of the pixel value.
13472        \param c C-coordinate of the pixel value.
13473        \note
13474        - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns
13475          the value of the nearest pixel in the image instance, regarding the specified X-coordinate.
13476        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13477          \c _linear_atX(float,int,int,int).
13478        \warning
13479        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13480     **/
13481     Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
13482       if (is_empty())
13483         throw CImgInstanceException(_cimg_instance
13484                                     "linear_atX(): Empty instance.",
13485                                     cimg_instance);
13486 
13487       return _linear_atX(fx,y,z,c);
13488     }
13489 
13490     Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
13491       const float
13492         nfx = cimg::cut(fx,0,width() - 1);
13493       const unsigned int
13494         x = (unsigned int)nfx;
13495       const float
13496         dx = nfx - x;
13497       const unsigned int
13498         nx = dx>0?x + 1:x;
13499       const Tfloat
13500         Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
13501       return Ic + dx*(In - Ic);
13502     }
13503 
13504     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
13505     /**
13506        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
13507        boundary checking are achieved both for X and Y-coordinates.
13508     **/
13509     Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
13510       const int
13511         x = (int)fx - (fx>=0?0:1), nx = x + 1,
13512         y = (int)fy - (fy>=0?0:1), ny = y + 1;
13513       const float
13514         dx = fx - x,
13515         dy = fy - y;
13516       const Tfloat
13517         Icc = (Tfloat)atXY(x,y,z,c,out_value),  Inc = (Tfloat)atXY(nx,y,z,c,out_value),
13518         Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
13519       return Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc);
13520     }
13521 
13522     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates.
13523     /**
13524        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
13525        are achieved both for X and Y-coordinates.
13526        \note
13527        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13528          \c _linear_atXY(float,float,int,int).
13529     **/
13530     Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
13531       if (is_empty())
13532         throw CImgInstanceException(_cimg_instance
13533                                     "linear_atXY(): Empty instance.",
13534                                     cimg_instance);
13535 
13536       return _linear_atXY(fx,fy,z,c);
13537     }
13538 
13539     Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
13540       const float
13541         nfx = cimg::cut(fx,0,width() - 1),
13542         nfy = cimg::cut(fy,0,height() - 1);
13543       const unsigned int
13544         x = (unsigned int)nfx,
13545         y = (unsigned int)nfy;
13546       const float
13547         dx = nfx - x,
13548         dy = nfy - y;
13549       const unsigned int
13550         nx = dx>0?x + 1:x,
13551         ny = dy>0?y + 1:y;
13552       const Tfloat
13553         Icc = (Tfloat)(*this)(x,y,z,c),  Inc = (Tfloat)(*this)(nx,y,z,c),
13554         Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
13555       return Icc + dx*(Inc - Icc + dy*(Icc + Inn - Icn - Inc)) + dy*(Icn - Icc);
13556     }
13557 
13558     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
13559     /**
13560        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
13561        boundary checking are achieved both for X,Y and Z-coordinates.
13562     **/
13563     Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
13564       const int
13565         x = (int)fx - (fx>=0?0:1), nx = x + 1,
13566         y = (int)fy - (fy>=0?0:1), ny = y + 1,
13567         z = (int)fz - (fz>=0?0:1), nz = z + 1;
13568       const float
13569         dx = fx - x,
13570         dy = fy - y,
13571         dz = fz - z;
13572       const Tfloat
13573         Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
13574         Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
13575         Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
13576         Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
13577       return Iccc +
13578         dx*(Incc - Iccc +
13579             dy*(Iccc + Innc - Icnc - Incc +
13580                 dz*(Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)) +
13581             dz*(Iccc + Incn - Iccn - Incc)) +
13582         dy*(Icnc - Iccc +
13583             dz*(Iccc + Icnn - Iccn - Icnc)) +
13584         dz*(Iccn - Iccc);
13585     }
13586 
13587     //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
13588     /**
13589        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
13590        are achieved both for X,Y and Z-coordinates.
13591        \note
13592        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13593          \c _linear_atXYZ(float,float,float,int).
13594     **/
13595     Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
13596       if (is_empty())
13597         throw CImgInstanceException(_cimg_instance
13598                                     "linear_atXYZ(): Empty instance.",
13599                                     cimg_instance);
13600 
13601       return _linear_atXYZ(fx,fy,fz,c);
13602     }
13603 
13604     Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
13605       const float
13606         nfx = cimg::cut(fx,0,width() - 1),
13607         nfy = cimg::cut(fy,0,height() - 1),
13608         nfz = cimg::cut(fz,0,depth() - 1);
13609       const unsigned int
13610         x = (unsigned int)nfx,
13611         y = (unsigned int)nfy,
13612         z = (unsigned int)nfz;
13613       const float
13614         dx = nfx - x,
13615         dy = nfy - y,
13616         dz = nfz - z;
13617       const unsigned int
13618         nx = dx>0?x + 1:x,
13619         ny = dy>0?y + 1:y,
13620         nz = dz>0?z + 1:z;
13621       const Tfloat
13622         Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
13623         Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
13624         Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
13625         Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
13626       return Iccc +
13627         dx*(Incc - Iccc +
13628             dy*(Iccc + Innc - Icnc - Incc +
13629                 dz*(Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)) +
13630             dz*(Iccc + Incn - Iccn - Incc)) +
13631         dy*(Icnc - Iccc +
13632             dz*(Iccc + Icnn - Iccn - Icnc)) +
13633         dz*(Iccn - Iccc);
13634     }
13635 
13636     //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates.
13637     /**
13638        Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
13639        boundary checking are achieved for all X,Y,Z and C-coordinates.
13640     **/
13641     Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const {
13642       const int
13643         x = (int)fx - (fx>=0?0:1), nx = x + 1,
13644         y = (int)fy - (fy>=0?0:1), ny = y + 1,
13645         z = (int)fz - (fz>=0?0:1), nz = z + 1,
13646         c = (int)fc - (fc>=0?0:1), nc = c + 1;
13647       const float
13648         dx = fx - x,
13649         dy = fy - y,
13650         dz = fz - z,
13651         dc = fc - c;
13652       const Tfloat
13653         Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
13654         Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
13655         Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
13656         Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
13657         Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
13658         Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
13659         Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
13660         Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
13661       return Icccc +
13662         dx*(Inccc - Icccc +
13663             dy*(Icccc + Inncc - Icncc - Inccc +
13664                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
13665                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
13666                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
13667                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
13668             dz*(Icccc + Incnc - Iccnc - Inccc +
13669                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
13670             dc*(Icccc + Inccn - Inccc - Icccn)) +
13671         dy*(Icncc - Icccc +
13672             dz*(Icccc + Icnnc - Iccnc - Icncc +
13673                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
13674             dc*(Icccc + Icncn - Icncc - Icccn)) +
13675         dz*(Iccnc - Icccc +
13676             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
13677         dc*(Icccn  -Icccc);
13678     }
13679 
13680     //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates.
13681     /**
13682        Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
13683        are achieved for all X,Y,Z and C-coordinates.
13684        \note
13685        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13686          \c _linear_atXYZC(float,float,float,float).
13687     **/
13688     Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
13689       if (is_empty())
13690         throw CImgInstanceException(_cimg_instance
13691                                     "linear_atXYZC(): Empty instance.",
13692                                     cimg_instance);
13693 
13694       return _linear_atXYZC(fx,fy,fz,fc);
13695     }
13696 
13697     Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
13698       const float
13699         nfx = cimg::cut(fx,0,width() - 1),
13700         nfy = cimg::cut(fy,0,height() - 1),
13701         nfz = cimg::cut(fz,0,depth() - 1),
13702         nfc = cimg::cut(fc,0,spectrum() - 1);
13703       const unsigned int
13704         x = (unsigned int)nfx,
13705         y = (unsigned int)nfy,
13706         z = (unsigned int)nfz,
13707         c = (unsigned int)nfc;
13708       const float
13709         dx = nfx - x,
13710         dy = nfy - y,
13711         dz = nfz - z,
13712         dc = nfc - c;
13713       const unsigned int
13714         nx = dx>0?x + 1:x,
13715         ny = dy>0?y + 1:y,
13716         nz = dz>0?z + 1:z,
13717         nc = dc>0?c + 1:c;
13718       const Tfloat
13719         Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
13720         Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
13721         Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
13722         Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
13723         Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
13724         Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
13725         Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
13726         Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
13727       return Icccc +
13728         dx*(Inccc - Icccc +
13729             dy*(Icccc + Inncc - Icncc - Inccc +
13730                 dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
13731                     dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
13732                         Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
13733                 dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
13734             dz*(Icccc + Incnc - Iccnc - Inccc +
13735                 dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
13736             dc*(Icccc + Inccn - Inccc - Icccn)) +
13737         dy*(Icncc - Icccc +
13738             dz*(Icccc + Icnnc - Iccnc - Icncc +
13739                 dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
13740             dc*(Icccc + Icncn - Icncc - Icccn)) +
13741         dz*(Iccnc - Icccc +
13742             dc*(Icccc + Iccnn - Iccnc - Icccn)) +
13743         dc*(Icccn - Icccc);
13744     }
13745 
13746     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
13747     /**
13748        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
13749        or a specified default value in case of out-of-bounds access along the X-axis.
13750        The cubic interpolation uses Hermite splines.
13751        \param fx d X-coordinate of the pixel value (float-valued).
13752        \param y Y-coordinate of the pixel value.
13753        \param z Z-coordinate of the pixel value.
13754        \param c C-coordinate of the pixel value.
13755        \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
13756        \note
13757        - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is
13758          approximated by a \e cubic interpolation along the X-axis.
13759        - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
13760        \warning
13761        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13762     **/
13763     Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
13764       const int
13765         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
13766       const float
13767         dx = fx - x;
13768       const Tfloat
13769         Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
13770         In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
13771       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));
13772     }
13773 
13774     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
13775     /**
13776        Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the
13777        min/max range of the datatype \c T.
13778     **/
13779     T cubic_cut_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
13780       return cimg::type<T>::cut(cubic_atX(fx,y,z,c,out_value));
13781     }
13782 
13783     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
13784     /**
13785        Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
13786        or the value of the nearest pixel location in the image instance in case of out-of-bounds access
13787        along the X-axis. The cubic interpolation uses Hermite splines.
13788        \param fx X-coordinate of the pixel value (float-valued).
13789        \param y Y-coordinate of the pixel value.
13790        \param z Z-coordinate of the pixel value.
13791        \param c C-coordinate of the pixel value.
13792        \note
13793        - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is
13794          approximated by a cubic interpolation along the X-axis.
13795        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13796          \c _cubic_atX(float,int,int,int).
13797        \warning
13798        - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
13799     **/
13800     Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
13801       if (is_empty())
13802         throw CImgInstanceException(_cimg_instance
13803                                     "cubic_atX(): Empty instance.",
13804                                     cimg_instance);
13805       return _cubic_atX(fx,y,z,c);
13806     }
13807 
13808     Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
13809       const float
13810         nfx = cimg::cut(fx,0,width() - 1);
13811       const int
13812         x = (int)nfx;
13813       const float
13814         dx = nfx - x;
13815       const int
13816         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2;
13817       const Tfloat
13818         Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
13819         In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
13820       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));
13821     }
13822 
13823     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
13824     /**
13825        Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the
13826        min/max range of the datatype \c T.
13827     **/
13828     T cubic_cut_atX(const float fx, const int y, const int z, const int c) const {
13829       return cimg::type<T>::cut(cubic_atX(fx,y,z,c));
13830     }
13831 
13832     T _cubic_cut_atX(const float fx, const int y, const int z, const int c) const {
13833       return cimg::type<T>::cut(_cubic_atX(fx,y,z,c));
13834     }
13835 
13836     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
13837     /**
13838        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
13839        are achieved both for X and Y-coordinates.
13840     **/
13841     Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
13842       const int
13843         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
13844         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
13845       const float dx = fx - x, dy = fy - y;
13846       const Tfloat
13847         Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value),
13848         Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
13849         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)),
13850         Ipc = (Tfloat)atXY(px,y,z,c,out_value),  Icc = (Tfloat)atXY(x, y,z,c,out_value),
13851         Inc = (Tfloat)atXY(nx,y,z,c,out_value),  Iac = (Tfloat)atXY(ax,y,z,c,out_value),
13852         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)),
13853         Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value),
13854         Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
13855         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)),
13856         Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value),
13857         Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
13858         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));
13859       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));
13860     }
13861 
13862     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates.
13863     /**
13864        Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the
13865        min/max range of the datatype \c T.
13866     **/
13867     T cubic_cut_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
13868       return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c,out_value));
13869     }
13870 
13871     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
13872     /**
13873        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
13874        are achieved for both X and Y-coordinates.
13875        \note
13876        - If you know your image instance is \e not empty, you may rather use the slightly faster method
13877        \c _cubic_atXY(float,float,int,int).
13878     **/
13879     Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
13880       if (is_empty())
13881         throw CImgInstanceException(_cimg_instance
13882                                     "cubic_atXY(): Empty instance.",
13883                                     cimg_instance);
13884       return _cubic_atXY(fx,fy,z,c);
13885     }
13886 
13887     Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
13888       const float
13889         nfx = cimg::cut(fx,0,width() - 1),
13890         nfy = cimg::cut(fy,0,height() - 1);
13891       const int x = (int)nfx, y = (int)nfy;
13892       const float dx = nfx - x, dy = nfy - y;
13893       const int
13894         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
13895         py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2;
13896       const Tfloat
13897         Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
13898         Iap = (Tfloat)(*this)(ax,py,z,c),
13899         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)),
13900         Ipc = (Tfloat)(*this)(px,y,z,c),  Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
13901         Iac = (Tfloat)(*this)(ax,y,z,c),
13902         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)),
13903         Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
13904         Ian = (Tfloat)(*this)(ax,ny,z,c),
13905         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)),
13906         Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
13907         Iaa = (Tfloat)(*this)(ax,ay,z,c),
13908         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));
13909       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));
13910     }
13911 
13912     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates.
13913     /**
13914        Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the
13915        min/max range of the datatype \c T.
13916     **/
13917     T cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const {
13918       return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c));
13919     }
13920 
13921     T _cubic_cut_atXY(const float fx, const float fy, const int z, const int c) const {
13922       return cimg::type<T>::cut(_cubic_atXY(fx,fy,z,c));
13923     }
13924 
13925     //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
13926     /**
13927        Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
13928        are achieved both for X,Y and Z-coordinates.
13929     **/
13930     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
13931       const int
13932         x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
13933         y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
13934         z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
13935       const float dx = fx - x, dy = fy - y, dz = fz - z;
13936       const Tfloat
13937         Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
13938         Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
13939         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
13940                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
13941         Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value),  Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
13942         Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value),  Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
13943         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
13944                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
13945         Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
13946         Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
13947         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
13948                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
13949         Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
13950         Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
13951         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
13952                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
13953         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
13954                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
13955         Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
13956         Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
13957         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
13958                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
13959         Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value),  Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
13960         Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),  Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
13961         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
13962                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
13963         Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
13964         Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
13965         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
13966                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
13967         Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
13968         Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
13969         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
13970                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
13971         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
13972                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
13973         Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
13974         Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
13975         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
13976                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
13977         Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value),  Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
13978         Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),  Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
13979         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
13980                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
13981         Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
13982         Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
13983         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
13984                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
13985         Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
13986         Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
13987         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
13988                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
13989         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
13990                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
13991         Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
13992         Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
13993         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
13994                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
13995         Ipca = (Tfloat)atXYZ(px,y,az,c,out_value),  Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
13996         Inca = (Tfloat)atXYZ(nx,y,az,c,out_value),  Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
13997         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
13998                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
13999         Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
14000         Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
14001         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
14002                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
14003         Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
14004         Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
14005         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
14006                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
14007         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
14008                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
14009       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));
14010     }
14011 
14012     //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates.
14013     /**
14014        Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay
14015        in the min/max range of the datatype \c T.
14016     **/
14017     T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
14018       return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c,out_value));
14019     }
14020 
14021     //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
14022     /**
14023        Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
14024        are achieved both for X,Y and Z-coordinates.
14025        \note
14026        - If you know your image instance is \e not empty, you may rather use the slightly faster method
14027          \c _cubic_atXYZ(float,float,float,int).
14028     **/
14029     Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
14030       if (is_empty())
14031         throw CImgInstanceException(_cimg_instance
14032                                     "cubic_atXYZ(): Empty instance.",
14033                                     cimg_instance);
14034       return _cubic_atXYZ(fx,fy,fz,c);
14035     }
14036 
14037     Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
14038       const float
14039         nfx = cimg::cut(fx,0,width() - 1),
14040         nfy = cimg::cut(fy,0,height() - 1),
14041         nfz = cimg::cut(fz,0,depth() - 1);
14042       const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
14043       const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
14044       const int
14045         px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
14046         py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2,
14047         pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2;
14048       const Tfloat
14049         Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
14050         Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
14051         Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
14052                            dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
14053         Ipcp = (Tfloat)(*this)(px,y,pz,c),  Iccp = (Tfloat)(*this)(x, y,pz,c),
14054         Incp = (Tfloat)(*this)(nx,y,pz,c),  Iacp = (Tfloat)(*this)(ax,y,pz,c),
14055         Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
14056                            dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
14057         Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
14058         Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
14059         Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
14060                            dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
14061         Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
14062         Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
14063         Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
14064                            dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
14065         Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
14066                          dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
14067         Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
14068         Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
14069         Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
14070                            dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
14071         Ipcc = (Tfloat)(*this)(px,y,z,c),  Iccc = (Tfloat)(*this)(x, y,z,c),
14072         Incc = (Tfloat)(*this)(nx,y,z,c),  Iacc = (Tfloat)(*this)(ax,y,z,c),
14073         Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
14074                            dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
14075         Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
14076         Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
14077         Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
14078                            dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
14079         Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
14080         Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
14081         Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
14082                            dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
14083         Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
14084                          dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
14085         Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
14086         Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
14087         Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
14088                            dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
14089         Ipcn = (Tfloat)(*this)(px,y,nz,c),  Iccn = (Tfloat)(*this)(x, y,nz,c),
14090         Incn = (Tfloat)(*this)(nx,y,nz,c),  Iacn = (Tfloat)(*this)(ax,y,nz,c),
14091         Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
14092                            dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
14093         Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
14094         Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
14095         Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
14096                            dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
14097         Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
14098         Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
14099         Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
14100                            dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
14101         In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
14102                          dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
14103         Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
14104         Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
14105         Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
14106                            dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
14107         Ipca = (Tfloat)(*this)(px,y,az,c),  Icca = (Tfloat)(*this)(x, y,az,c),
14108         Inca = (Tfloat)(*this)(nx,y,az,c),  Iaca = (Tfloat)(*this)(ax,y,az,c),
14109         Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
14110                            dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
14111         Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
14112         Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
14113         Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
14114                            dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
14115         Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
14116         Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
14117         Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
14118                            dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
14119         Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
14120                          dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
14121       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));
14122     }
14123 
14124     //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates.
14125     /**
14126        Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the
14127        min/max range of the datatype \c T.
14128     **/
14129     T cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const {
14130       return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c));
14131     }
14132 
14133     T _cubic_cut_atXYZ(const float fx, const float fy, const float fz, const int c) const {
14134       return cimg::type<T>::cut(_cubic_atXYZ(fx,fy,fz,c));
14135     }
14136 
14137     //! Set pixel value, using linear interpolation for the X-coordinates.
14138     /**
14139        Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that
14140        the value is spread amongst several neighbors if the pixel coordinates are float-valued.
14141        \param value Pixel value to set.
14142        \param fx X-coordinate of the pixel value (float-valued).
14143        \param y Y-coordinate of the pixel value.
14144        \param z Z-coordinate of the pixel value.
14145        \param c C-coordinate of the pixel value.
14146        \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image
14147          pixel(s).
14148        \return A reference to the current image instance.
14149        \note
14150        - Calling this method with out-of-bounds coordinates does nothing.
14151     **/
14152     CImg<T>& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0,
14153                             const bool is_added=false) {
14154       const int
14155         x = (int)fx - (fx>=0?0:1), nx = x + 1;
14156       const float
14157         dx = fx - x;
14158       if (y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum()) {
14159         if (x>=0 && x<width()) {
14160           const float w1 = 1 - dx, w2 = is_added?1:(1 - w1);
14161           (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
14162         }
14163         if (nx>=0 && nx<width()) {
14164           const float w1 = dx, w2 = is_added?1:(1 - w1);
14165           (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
14166         }
14167       }
14168       return *this;
14169     }
14170 
14171     //! Set pixel value, using linear interpolation for the X and Y-coordinates.
14172     /**
14173        Similar to set_linear_atX(const T&,float,int,int,int,bool), except that the linear interpolation
14174        is achieved both for X and Y-coordinates.
14175     **/
14176     CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
14177                              const bool is_added=false) {
14178       const int
14179         x = (int)fx - (fx>=0?0:1), nx = x + 1,
14180         y = (int)fy - (fy>=0?0:1), ny = y + 1;
14181       const float
14182         dx = fx - x,
14183         dy = fy - y;
14184       if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
14185         if (y>=0 && y<height()) {
14186           if (x>=0 && x<width()) {
14187             const float w1 = (1 - dx)*(1 - dy), w2 = is_added?1:(1 - w1);
14188             (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
14189           }
14190           if (nx>=0 && nx<width()) {
14191             const float w1 = dx*(1 - dy), w2 = is_added?1:(1 - w1);
14192             (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
14193           }
14194         }
14195         if (ny>=0 && ny<height()) {
14196           if (x>=0 && x<width()) {
14197             const float w1 = (1 - dx)*dy, w2 = is_added?1:(1 - w1);
14198             (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
14199           }
14200           if (nx>=0 && nx<width()) {
14201             const float w1 = dx*dy, w2 = is_added?1:(1 - w1);
14202             (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
14203           }
14204         }
14205       }
14206       return *this;
14207     }
14208 
14209     //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
14210     /**
14211        Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation
14212        is achieved both for X,Y and Z-coordinates.
14213     **/
14214     CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
14215                               const bool is_added=false) {
14216       const int
14217         x = (int)fx - (fx>=0?0:1), nx = x + 1,
14218         y = (int)fy - (fy>=0?0:1), ny = y + 1,
14219         z = (int)fz - (fz>=0?0:1), nz = z + 1;
14220       const float
14221         dx = fx - x,
14222         dy = fy - y,
14223         dz = fz - z;
14224       if (c>=0 && c<spectrum()) {
14225         if (z>=0 && z<depth()) {
14226           if (y>=0 && y<height()) {
14227             if (x>=0 && x<width()) {
14228               const float w1 = (1 - dx)*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
14229               (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
14230             }
14231             if (nx>=0 && nx<width()) {
14232               const float w1 = dx*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
14233               (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
14234             }
14235           }
14236           if (ny>=0 && ny<height()) {
14237             if (x>=0 && x<width()) {
14238               const float w1 = (1 - dx)*dy*(1 - dz), w2 = is_added?1:(1 - w1);
14239               (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
14240             }
14241             if (nx>=0 && nx<width()) {
14242               const float w1 = dx*dy*(1 - dz), w2 = is_added?1:(1 - w1);
14243               (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
14244             }
14245           }
14246         }
14247         if (nz>=0 && nz<depth()) {
14248           if (y>=0 && y<height()) {
14249             if (x>=0 && x<width()) {
14250               const float w1 = (1 - dx)*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
14251               (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
14252             }
14253             if (nx>=0 && nx<width()) {
14254               const float w1 = dx*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
14255               (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
14256             }
14257           }
14258           if (ny>=0 && ny<height()) {
14259             if (x>=0 && x<width()) {
14260               const float w1 = (1 - dx)*dy*dz, w2 = is_added?1:(1 - w1);
14261               (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
14262             }
14263             if (nx>=0 && nx<width()) {
14264               const float w1 = dx*dy*dz, w2 = is_added?1:(1 - w1);
14265               (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
14266             }
14267           }
14268         }
14269       }
14270       return *this;
14271     }
14272 
14273     //! Return a C-string containing a list of all values of the image instance.
14274     /**
14275        Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values
14276        of the image instance (written in base 10), separated by specified \c separator character.
14277        \param separator A \c char character which specifies the separator between values in the returned C-string.
14278        \param max_size Maximum size of the returned image (or \c 0 if no limits are set).
14279        \param format For float/double-values, tell the printf format used to generate the ascii representation
14280          of the numbers (or \c 0 for default representation).
14281        \note
14282        - The returned image is never empty.
14283        - For an empty image instance, the returned string is <tt>""</tt>.
14284        - If \c max_size is equal to \c 0, there are no limits on the size of the returned string.
14285        - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off
14286          and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>.
14287     **/
14288     CImg<charT> value_string(const char separator=',', const unsigned int max_size=0,
14289                              const char *const format=0) const {
14290       if (is_empty() || max_size==1) return CImg<charT>(1,1,1,1,0);
14291       CImgList<charT> items;
14292       CImg<charT> s_item(256); *s_item = 0;
14293       const T *ptrs = _data;
14294       unsigned int string_size = 0;
14295       const char *const _format = format?format:cimg::type<T>::format();
14296       for (ulongT off = 0, siz = size(); off<siz && (!max_size || string_size<max_size); ++off) {
14297         const unsigned int printed_size = 1U + cimg_snprintf(s_item,s_item._width,_format,
14298                                                              cimg::type<T>::format(*(ptrs++)));
14299         CImg<charT> item(s_item._data,printed_size);
14300         item[printed_size - 1] = separator;
14301         item.move_to(items);
14302         if (max_size) string_size+=printed_size;
14303       }
14304       CImg<charT> res;
14305       (items>'x').move_to(res);
14306       if (max_size && res._width>=max_size) res.crop(0,max_size - 1);
14307       res.back() = 0;
14308       return res;
14309     }
14310 
14311     //@}
14312     //-------------------------------------
14313     //
14314     //! \name Instance Checking
14315     //@{
14316     //-------------------------------------
14317 
14318     //! Test shared state of the pixel buffer.
14319     /**
14320        Return \c true if image instance has a shared memory buffer, and \c false otherwise.
14321        \note
14322        - A shared image do not own his pixel buffer data() and will not deallocate it on destruction.
14323        - Most of the time, a \c CImg<T> image instance will \e not be shared.
14324        - A shared image can only be obtained by a limited set of constructors and methods (see list below).
14325     **/
14326     bool is_shared() const {
14327       return _is_shared;
14328     }
14329 
14330     //! Test if image instance is empty.
14331     /**
14332        Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions
14333        \c 0 x \c 0 x \c 0 x \c 0 and a pixel buffer pointer set to \c 0 (null pointer), and \c false otherwise.
14334     **/
14335     bool is_empty() const {
14336       return !(_data && _width && _height && _depth && _spectrum);
14337     }
14338 
14339     //! Test if image instance contains a 'inf' value.
14340     /**
14341        Return \c true, if image instance contains a 'inf' value, and \c false otherwise.
14342     **/
14343     bool is_inf() const {
14344       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
14345       return false;
14346     }
14347 
14348     //! Test if image instance contains a NaN value.
14349     /**
14350        Return \c true, if image instance contains a NaN value, and \c false otherwise.
14351     **/
14352     bool is_nan() const {
14353       if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
14354       return false;
14355     }
14356 
14357     //! Test if image width is equal to specified value.
14358     bool is_sameX(const unsigned int size_x) const {
14359       return _width==size_x;
14360     }
14361 
14362     //! Test if image width is equal to specified value.
14363     template<typename t>
14364     bool is_sameX(const CImg<t>& img) const {
14365       return is_sameX(img._width);
14366     }
14367 
14368     //! Test if image width is equal to specified value.
14369     bool is_sameX(const CImgDisplay& disp) const {
14370       return is_sameX(disp._width);
14371     }
14372 
14373     //! Test if image height is equal to specified value.
14374     bool is_sameY(const unsigned int size_y) const {
14375       return _height==size_y;
14376     }
14377 
14378     //! Test if image height is equal to specified value.
14379     template<typename t>
14380     bool is_sameY(const CImg<t>& img) const {
14381       return is_sameY(img._height);
14382     }
14383 
14384     //! Test if image height is equal to specified value.
14385     bool is_sameY(const CImgDisplay& disp) const {
14386       return is_sameY(disp._height);
14387     }
14388 
14389     //! Test if image depth is equal to specified value.
14390     bool is_sameZ(const unsigned int size_z) const {
14391       return _depth==size_z;
14392     }
14393 
14394     //! Test if image depth is equal to specified value.
14395     template<typename t>
14396     bool is_sameZ(const CImg<t>& img) const {
14397       return is_sameZ(img._depth);
14398     }
14399 
14400     //! Test if image spectrum is equal to specified value.
14401     bool is_sameC(const unsigned int size_c) const {
14402       return _spectrum==size_c;
14403     }
14404 
14405     //! Test if image spectrum is equal to specified value.
14406     template<typename t>
14407     bool is_sameC(const CImg<t>& img) const {
14408       return is_sameC(img._spectrum);
14409     }
14410 
14411     //! Test if image width and height are equal to specified values.
14412     /**
14413        Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified.
14414     **/
14415     bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
14416       return _width==size_x && _height==size_y;
14417     }
14418 
14419     //! Test if image width and height are the same as that of another image.
14420     /**
14421        Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified.
14422     **/
14423     template<typename t>
14424     bool is_sameXY(const CImg<t>& img) const {
14425       return is_sameXY(img._width,img._height);
14426     }
14427 
14428     //! Test if image width and height are the same as that of an existing display window.
14429     /**
14430        Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified.
14431     **/
14432     bool is_sameXY(const CImgDisplay& disp) const {
14433       return is_sameXY(disp._width,disp._height);
14434     }
14435 
14436     //! Test if image width and depth are equal to specified values.
14437     /**
14438        Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified.
14439     **/
14440     bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
14441       return _width==size_x && _depth==size_z;
14442     }
14443 
14444     //! Test if image width and depth are the same as that of another image.
14445     /**
14446        Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
14447     **/
14448     template<typename t>
14449     bool is_sameXZ(const CImg<t>& img) const {
14450       return is_sameXZ(img._width,img._depth);
14451     }
14452 
14453     //! Test if image width and spectrum are equal to specified values.
14454     /**
14455        Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified.
14456     **/
14457     bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
14458       return _width==size_x && _spectrum==size_c;
14459     }
14460 
14461     //! Test if image width and spectrum are the same as that of another image.
14462     /**
14463        Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14464     **/
14465     template<typename t>
14466     bool is_sameXC(const CImg<t>& img) const {
14467       return is_sameXC(img._width,img._spectrum);
14468     }
14469 
14470     //! Test if image height and depth are equal to specified values.
14471     /**
14472        Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified.
14473     **/
14474     bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
14475       return _height==size_y && _depth==size_z;
14476     }
14477 
14478     //! Test if image height and depth are the same as that of another image.
14479     /**
14480        Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
14481     **/
14482     template<typename t>
14483     bool is_sameYZ(const CImg<t>& img) const {
14484       return is_sameYZ(img._height,img._depth);
14485     }
14486 
14487     //! Test if image height and spectrum are equal to specified values.
14488     /**
14489        Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified.
14490     **/
14491     bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
14492       return _height==size_y && _spectrum==size_c;
14493     }
14494 
14495     //! Test if image height and spectrum are the same as that of another image.
14496     /**
14497        Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14498     **/
14499     template<typename t>
14500     bool is_sameYC(const CImg<t>& img) const {
14501       return is_sameYC(img._height,img._spectrum);
14502     }
14503 
14504     //! Test if image depth and spectrum are equal to specified values.
14505     /**
14506        Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified.
14507     **/
14508     bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
14509       return _depth==size_z && _spectrum==size_c;
14510     }
14511 
14512     //! Test if image depth and spectrum are the same as that of another image.
14513     /**
14514        Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14515     **/
14516     template<typename t>
14517     bool is_sameZC(const CImg<t>& img) const {
14518       return is_sameZC(img._depth,img._spectrum);
14519     }
14520 
14521     //! Test if image width, height and depth are equal to specified values.
14522     /**
14523        Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified.
14524     **/
14525     bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
14526       return is_sameXY(size_x,size_y) && _depth==size_z;
14527     }
14528 
14529     //! Test if image width, height and depth are the same as that of another image.
14530     /**
14531        Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
14532     **/
14533     template<typename t>
14534     bool is_sameXYZ(const CImg<t>& img) const {
14535       return is_sameXYZ(img._width,img._height,img._depth);
14536     }
14537 
14538     //! Test if image width, height and spectrum are equal to specified values.
14539     /**
14540        Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
14541     **/
14542     bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
14543       return is_sameXY(size_x,size_y) && _spectrum==size_c;
14544     }
14545 
14546     //! Test if image width, height and spectrum are the same as that of another image.
14547     /**
14548        Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14549     **/
14550     template<typename t>
14551     bool is_sameXYC(const CImg<t>& img) const {
14552       return is_sameXYC(img._width,img._height,img._spectrum);
14553     }
14554 
14555     //! Test if image width, depth and spectrum are equal to specified values.
14556     /**
14557        Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
14558     **/
14559     bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
14560       return is_sameXZ(size_x,size_z) && _spectrum==size_c;
14561     }
14562 
14563     //! Test if image width, depth and spectrum are the same as that of another image.
14564     /**
14565        Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14566     **/
14567     template<typename t>
14568     bool is_sameXZC(const CImg<t>& img) const {
14569       return is_sameXZC(img._width,img._depth,img._spectrum);
14570     }
14571 
14572     //! Test if image height, depth and spectrum are equal to specified values.
14573     /**
14574        Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
14575     **/
14576     bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
14577       return is_sameYZ(size_y,size_z) && _spectrum==size_c;
14578     }
14579 
14580     //! Test if image height, depth and spectrum are the same as that of another image.
14581     /**
14582        Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14583     **/
14584     template<typename t>
14585     bool is_sameYZC(const CImg<t>& img) const {
14586       return is_sameYZC(img._height,img._depth,img._spectrum);
14587     }
14588 
14589     //! Test if image width, height, depth and spectrum are equal to specified values.
14590     /**
14591        Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both
14592        verified.
14593     **/
14594     bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y,
14595                      const unsigned int size_z, const unsigned int size_c) const {
14596       return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
14597     }
14598 
14599     //! Test if image width, height, depth and spectrum are the same as that of another image.
14600     /**
14601        Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
14602     **/
14603     template<typename t>
14604     bool is_sameXYZC(const CImg<t>& img) const {
14605       return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
14606     }
14607 
14608     //! Test if specified coordinates are inside image bounds.
14609     /**
14610        Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance,
14611        and \c false otherwise.
14612        \param x X-coordinate of the pixel value.
14613        \param y Y-coordinate of the pixel value.
14614        \param z Z-coordinate of the pixel value.
14615        \param c C-coordinate of the pixel value.
14616        \note
14617        - Return \c true only if all these conditions are verified:
14618          - The image instance is \e not empty.
14619          - <tt>0<=x<=\ref width() - 1</tt>.
14620          - <tt>0<=y<=\ref height() - 1</tt>.
14621          - <tt>0<=z<=\ref depth() - 1</tt>.
14622          - <tt>0<=c<=\ref spectrum() - 1</tt>.
14623     **/
14624     bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
14625       return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
14626     }
14627 
14628     //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
14629     /**
14630        Return \c true, if specified reference refers to a pixel value inside bounds of the image instance,
14631        and \c false otherwise.
14632        \param pixel Reference to pixel value to test.
14633        \param[out] x X-coordinate of the pixel value, if test succeeds.
14634        \param[out] y Y-coordinate of the pixel value, if test succeeds.
14635        \param[out] z Z-coordinate of the pixel value, if test succeeds.
14636        \param[out] c C-coordinate of the pixel value, if test succeeds.
14637        \note
14638        - Useful to convert an offset to a buffer value into pixel value coordinates:
14639        \code
14640        const CImg<float> img(100,100,1,3);      // Construct a 100x100 RGB color image.
14641        const unsigned long offset = 1249;       // Offset to the pixel (49,12,0,0).
14642        unsigned int x,y,z,c;
14643        if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates.
14644          std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n",
14645                      offset,x,y,z,c);
14646        }
14647        \endcode
14648     **/
14649     template<typename t>
14650     bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
14651       const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
14652       const T *const ppixel = &pixel;
14653       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
14654       ulongT off = (ulongT)(ppixel - _data);
14655       const ulongT nc = off/whd;
14656       off%=whd;
14657       const ulongT nz = off/wh;
14658       off%=wh;
14659       const ulongT ny = off/_width, nx = off%_width;
14660       x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
14661       return true;
14662     }
14663 
14664     //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
14665     /**
14666        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set.
14667     **/
14668     template<typename t>
14669     bool contains(const T& pixel, t& x, t& y, t& z) const {
14670       const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
14671       const T *const ppixel = &pixel;
14672       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
14673       ulongT off = ((ulongT)(ppixel - _data))%whd;
14674       const ulongT nz = off/wh;
14675       off%=wh;
14676       const ulongT ny = off/_width, nx = off%_width;
14677       x = (t)nx; y = (t)ny; z = (t)nz;
14678       return true;
14679     }
14680 
14681     //! Test if pixel value is inside image bounds and get its X and Y-coordinates.
14682     /**
14683        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set.
14684     **/
14685     template<typename t>
14686     bool contains(const T& pixel, t& x, t& y) const {
14687       const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum;
14688       const T *const ppixel = &pixel;
14689       if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
14690       ulongT off = ((unsigned int)(ppixel - _data))%wh;
14691       const ulongT ny = off/_width, nx = off%_width;
14692       x = (t)nx; y = (t)ny;
14693       return true;
14694     }
14695 
14696     //! Test if pixel value is inside image bounds and get its X-coordinate.
14697     /**
14698        Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set.
14699     **/
14700     template<typename t>
14701     bool contains(const T& pixel, t& x) const {
14702       const T *const ppixel = &pixel;
14703       if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false;
14704       x = (t)(((ulongT)(ppixel - _data))%_width);
14705       return true;
14706     }
14707 
14708     //! Test if pixel value is inside image bounds.
14709     /**
14710        Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set.
14711     **/
14712     bool contains(const T& pixel) const {
14713       const T *const ppixel = &pixel;
14714       return !is_empty() && ppixel>=_data && ppixel<_data + size();
14715     }
14716 
14717     //! Test if pixel buffers of instance and input images overlap.
14718     /**
14719        Return \c true, if pixel buffers attached to image instance and input image \c img overlap,
14720        and \c false otherwise.
14721        \param img Input image to compare with.
14722        \note
14723        - Buffer overlapping may happen when manipulating \e shared images.
14724        - If two image buffers overlap, operating on one of the image will probably modify the other one.
14725        - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others.
14726        \par Example
14727        \code
14728        const CImg<float>
14729          img1("reference.jpg"),             // Load RGB-color image.
14730          img2 = img1.get_shared_channel(1); // Get shared version of the green channel.
14731        if (img1.is_overlapped(img2)) {      // Test succeeds, 'img1' and 'img2' overlaps.
14732          std::printf("Buffers overlap!\n");
14733        }
14734        \endcode
14735     **/
14736     template<typename t>
14737     bool is_overlapped(const CImg<t>& img) const {
14738       const ulongT csiz = size(), isiz = img.size();
14739       return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
14740     }
14741 
14742     //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3d object.
14743     /**
14744        Return \c true is the 3d object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a
14745        valid 3d object, and \c false otherwise. The vertex coordinates are defined by the instance image.
14746        \param primitives List of primitives of the 3d object.
14747        \param colors List of colors of the 3d object.
14748        \param opacities List (or image) of opacities of the 3d object.
14749        \param full_check Tells if full checking of the 3d object must be performed.
14750        \param[out] error_message C-string to contain the error message, if the test does not succeed.
14751        \note
14752        - Set \c full_checking to \c false to speed-up the 3d object checking. In this case, only the size of
14753          each 3d object component is checked.
14754        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
14755     **/
14756     template<typename tp, typename tc, typename to>
14757     bool is_object3d(const CImgList<tp>& primitives,
14758                      const CImgList<tc>& colors,
14759                      const to& opacities,
14760                      const bool full_check=true,
14761                      char *const error_message=0) const {
14762       if (error_message) *error_message = 0;
14763 
14764       // Check consistency for the particular case of an empty 3d object.
14765       if (is_empty()) {
14766         if (primitives || colors || opacities) {
14767           if (error_message) cimg_sprintf(error_message,
14768                                           "3d object (%u,%u) defines no vertices but %u primitives, "
14769                                           "%u colors and %lu opacities",
14770                                           _width,primitives._width,primitives._width,
14771                                           colors._width,(unsigned long)opacities.size());
14772           return false;
14773         }
14774         return true;
14775       }
14776 
14777       // Check consistency of vertices.
14778       if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions.
14779         if (error_message) cimg_sprintf(error_message,
14780                                         "3d object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)",
14781                                         _width,primitives._width,_width,_height,_depth,_spectrum);
14782         return false;
14783       }
14784       if (colors._width>primitives._width + 1) {
14785         if (error_message) cimg_sprintf(error_message,
14786                                         "3d object (%u,%u) defines %u colors",
14787                                         _width,primitives._width,colors._width);
14788         return false;
14789       }
14790       if (opacities.size()>primitives._width) {
14791         if (error_message) cimg_sprintf(error_message,
14792                                         "3d object (%u,%u) defines %lu opacities",
14793                                         _width,primitives._width,(unsigned long)opacities.size());
14794         return false;
14795       }
14796       if (!full_check) return true;
14797 
14798       // Check consistency of primitives.
14799       cimglist_for(primitives,l) {
14800         const CImg<tp>& primitive = primitives[l];
14801         const unsigned int psiz = (unsigned int)primitive.size();
14802         switch (psiz) {
14803         case 1 : { // Point.
14804           const unsigned int i0 = (unsigned int)primitive(0);
14805           if (i0>=_width) {
14806             if (error_message) cimg_sprintf(error_message,
14807                                             "3d object (%u,%u) refers to invalid vertex indice %u in "
14808                                             "point primitive [%u]",
14809                                             _width,primitives._width,i0,l);
14810             return false;
14811           }
14812         } break;
14813         case 5 : { // Sphere.
14814           const unsigned int
14815             i0 = (unsigned int)primitive(0),
14816             i1 = (unsigned int)primitive(1);
14817           if (i0>=_width || i1>=_width) {
14818             if (error_message) cimg_sprintf(error_message,
14819                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in "
14820                                             "sphere primitive [%u]",
14821                                             _width,primitives._width,i0,i1,l);
14822             return false;
14823           }
14824         } break;
14825         case 2 : case 6 : { // Segment.
14826           const unsigned int
14827             i0 = (unsigned int)primitive(0),
14828             i1 = (unsigned int)primitive(1);
14829           if (i0>=_width || i1>=_width) {
14830             if (error_message) cimg_sprintf(error_message,
14831                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in "
14832                                             "segment primitive [%u]",
14833                                             _width,primitives._width,i0,i1,l);
14834             return false;
14835           }
14836         } break;
14837         case 3 : case 9 : { // Triangle.
14838           const unsigned int
14839             i0 = (unsigned int)primitive(0),
14840             i1 = (unsigned int)primitive(1),
14841             i2 = (unsigned int)primitive(2);
14842           if (i0>=_width || i1>=_width || i2>=_width) {
14843             if (error_message) cimg_sprintf(error_message,
14844                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
14845                                             "triangle primitive [%u]",
14846                                             _width,primitives._width,i0,i1,i2,l);
14847             return false;
14848           }
14849         } break;
14850         case 4 : case 12 : { // Quadrangle.
14851           const unsigned int
14852             i0 = (unsigned int)primitive(0),
14853             i1 = (unsigned int)primitive(1),
14854             i2 = (unsigned int)primitive(2),
14855             i3 = (unsigned int)primitive(3);
14856           if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
14857             if (error_message) cimg_sprintf(error_message,
14858                                             "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
14859                                             "quadrangle primitive [%u]",
14860                                             _width,primitives._width,i0,i1,i2,i3,l);
14861             return false;
14862           }
14863         } break;
14864         default :
14865           if (error_message) cimg_sprintf(error_message,
14866                                           "3d object (%u,%u) defines an invalid primitive [%u] of size %u",
14867                                           _width,primitives._width,l,(unsigned int)psiz);
14868           return false;
14869         }
14870       }
14871 
14872       // Check consistency of colors.
14873       cimglist_for(colors,c) {
14874         const CImg<tc>& color = colors[c];
14875         if (!color) {
14876           if (error_message) cimg_sprintf(error_message,
14877                                           "3d object (%u,%u) defines no color for primitive [%u]",
14878                                           _width,primitives._width,c);
14879           return false;
14880         }
14881       }
14882 
14883       // Check consistency of light texture.
14884       if (colors._width>primitives._width) {
14885         const CImg<tc> &light = colors.back();
14886         if (!light || light._depth>1) {
14887           if (error_message) cimg_sprintf(error_message,
14888                                           "3d object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)",
14889                                           _width,primitives._width,light._width,
14890                                           light._height,light._depth,light._spectrum);
14891           return false;
14892         }
14893       }
14894 
14895       return true;
14896     }
14897 
14898     //! Test if image instance represents a valid serialization of a 3d object.
14899     /**
14900        Return \c true if the image instance represents a valid serialization of a 3d object, and \c false otherwise.
14901        \param full_check Tells if full checking of the instance must be performed.
14902        \param[out] error_message C-string to contain the error message, if the test does not succeed.
14903        \note
14904        - Set \c full_check to \c false to speed-up the 3d object checking. In this case, only the size of
14905          each 3d object component is checked.
14906        - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
14907     **/
14908     bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
14909       if (error_message) *error_message = 0;
14910 
14911       // Check instance dimension and header.
14912       if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
14913         if (error_message) cimg_sprintf(error_message,
14914                                         "CImg3d has invalid dimensions (%u,%u,%u,%u)",
14915                                         _width,_height,_depth,_spectrum);
14916         return false;
14917       }
14918       const T *ptrs = _data, *const ptre = end();
14919       if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
14920           !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
14921         if (error_message) cimg_sprintf(error_message,
14922                                         "CImg3d header not found");
14923         return false;
14924       }
14925       const unsigned int
14926         nb_points = cimg::float2uint((float)*(ptrs++)),
14927         nb_primitives = cimg::float2uint((float)*(ptrs++));
14928 
14929       // Check consistency of number of vertices / primitives.
14930       if (!full_check) {
14931         const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives;
14932         if (_data + minimal_size>ptre) {
14933           if (error_message) cimg_sprintf(error_message,
14934                                           "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected",
14935                                           nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size);
14936           return false;
14937         }
14938       }
14939 
14940       // Check consistency of vertex data.
14941       if (!nb_points) {
14942         if (nb_primitives) {
14943           if (error_message) cimg_sprintf(error_message,
14944                                           "CImg3d (%u,%u) defines no vertices but %u primitives",
14945                                           nb_points,nb_primitives,nb_primitives);
14946           return false;
14947         }
14948         if (ptrs!=ptre) {
14949           if (error_message) cimg_sprintf(error_message,
14950                                           "CImg3d (%u,%u) is an empty object but contains %u value%s "
14951                                           "more than expected",
14952                                           nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
14953           return false;
14954         }
14955         return true;
14956       }
14957       if (ptrs + 3*nb_points>ptre) {
14958         if (error_message) cimg_sprintf(error_message,
14959                                         "CImg3d (%u,%u) defines only %u vertices data",
14960                                         nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3);
14961         return false;
14962       }
14963       ptrs+=3*nb_points;
14964 
14965       // Check consistency of primitive data.
14966       if (ptrs==ptre) {
14967         if (error_message) cimg_sprintf(error_message,
14968                                         "CImg3d (%u,%u) defines %u vertices but no primitive",
14969                                         nb_points,nb_primitives,nb_points);
14970         return false;
14971       }
14972 
14973       if (!full_check) return true;
14974 
14975       for (unsigned int p = 0; p<nb_primitives; ++p) {
14976         const unsigned int nb_inds = (unsigned int)*(ptrs++);
14977         switch (nb_inds) {
14978         case 1 : { // Point.
14979           const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
14980           if (i0>=nb_points) {
14981             if (error_message) cimg_sprintf(error_message,
14982                                             "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive [%u]",
14983                                             nb_points,nb_primitives,i0,p);
14984             return false;
14985           }
14986         } break;
14987         case 5 : { // Sphere.
14988           const unsigned int
14989             i0 = cimg::float2uint((float)*(ptrs++)),
14990             i1 = cimg::float2uint((float)*(ptrs++));
14991           ptrs+=3;
14992           if (i0>=nb_points || i1>=nb_points) {
14993             if (error_message) cimg_sprintf(error_message,
14994                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
14995                                             "sphere primitive [%u]",
14996                                             nb_points,nb_primitives,i0,i1,p);
14997             return false;
14998           }
14999         } break;
15000         case 2 : case 6 : { // Segment.
15001           const unsigned int
15002             i0 = cimg::float2uint((float)*(ptrs++)),
15003             i1 = cimg::float2uint((float)*(ptrs++));
15004           if (nb_inds==6) ptrs+=4;
15005           if (i0>=nb_points || i1>=nb_points) {
15006             if (error_message) cimg_sprintf(error_message,
15007                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
15008                                             "segment primitive [%u]",
15009                                             nb_points,nb_primitives,i0,i1,p);
15010             return false;
15011           }
15012         } break;
15013         case 3 : case 9 : { // Triangle.
15014           const unsigned int
15015             i0 = cimg::float2uint((float)*(ptrs++)),
15016             i1 = cimg::float2uint((float)*(ptrs++)),
15017             i2 = cimg::float2uint((float)*(ptrs++));
15018           if (nb_inds==9) ptrs+=6;
15019           if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
15020             if (error_message) cimg_sprintf(error_message,
15021                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
15022                                             "triangle primitive [%u]",
15023                                             nb_points,nb_primitives,i0,i1,i2,p);
15024             return false;
15025           }
15026         } break;
15027         case 4 : case 12 : { // Quadrangle.
15028           const unsigned int
15029             i0 = cimg::float2uint((float)*(ptrs++)),
15030             i1 = cimg::float2uint((float)*(ptrs++)),
15031             i2 = cimg::float2uint((float)*(ptrs++)),
15032             i3 = cimg::float2uint((float)*(ptrs++));
15033           if (nb_inds==12) ptrs+=8;
15034           if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
15035             if (error_message) cimg_sprintf(error_message,
15036                                             "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
15037                                             "quadrangle primitive [%u]",
15038                                             nb_points,nb_primitives,i0,i1,i2,i3,p);
15039             return false;
15040           }
15041         } break;
15042         default :
15043           if (error_message) cimg_sprintf(error_message,
15044                                           "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u",
15045                                           nb_points,nb_primitives,p,nb_inds);
15046           return false;
15047         }
15048         if (ptrs>ptre) {
15049           if (error_message) cimg_sprintf(error_message,
15050                                           "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], "
15051                                           "%u values missing",
15052                                           nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre));
15053           return false;
15054         }
15055       }
15056 
15057       // Check consistency of color data.
15058       if (ptrs==ptre) {
15059         if (error_message) cimg_sprintf(error_message,
15060                                         "CImg3d (%u,%u) defines no color/texture data",
15061                                         nb_points,nb_primitives);
15062         return false;
15063       }
15064       for (unsigned int c = 0; c<nb_primitives; ++c) {
15065         if (*(ptrs++)!=(T)-128) ptrs+=2;
15066         else if ((ptrs+=3)<ptre) {
15067           const unsigned int
15068             w = (unsigned int)*(ptrs - 3),
15069             h = (unsigned int)*(ptrs - 2),
15070             s = (unsigned int)*(ptrs - 1);
15071           if (!h && !s) {
15072             if (w>=c) {
15073               if (error_message) cimg_sprintf(error_message,
15074                                               "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u "
15075                                               "for primitive [%u]",
15076                                               nb_points,nb_primitives,w,c);
15077               return false;
15078             }
15079           } else ptrs+=w*h*s;
15080         }
15081         if (ptrs>ptre) {
15082           if (error_message) cimg_sprintf(error_message,
15083                                           "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], "
15084                                           "%u values missing",
15085                                           nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre));
15086           return false;
15087         }
15088       }
15089 
15090       // Check consistency of opacity data.
15091       if (ptrs==ptre) {
15092         if (error_message) cimg_sprintf(error_message,
15093                                         "CImg3d (%u,%u) defines no opacity data",
15094                                         nb_points,nb_primitives);
15095         return false;
15096       }
15097       for (unsigned int o = 0; o<nb_primitives; ++o) {
15098         if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
15099           const unsigned int
15100             w = (unsigned int)*(ptrs - 3),
15101             h = (unsigned int)*(ptrs - 2),
15102             s = (unsigned int)*(ptrs - 1);
15103           if (!h && !s) {
15104             if (w>=o) {
15105               if (error_message) cimg_sprintf(error_message,
15106                                               "CImg3d (%u,%u) refers to invalid shared opacity indice %u "
15107                                               "for primitive [%u]",
15108                                               nb_points,nb_primitives,w,o);
15109               return false;
15110             }
15111           } else ptrs+=w*h*s;
15112         }
15113         if (ptrs>ptre) {
15114           if (error_message) cimg_sprintf(error_message,
15115                                           "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]",
15116                                           nb_points,nb_primitives,o);
15117           return false;
15118         }
15119       }
15120 
15121       // Check end of data.
15122       if (ptrs<ptre) {
15123         if (error_message) cimg_sprintf(error_message,
15124                                         "CImg3d (%u,%u) contains %u value%s more than expected",
15125                                         nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
15126         return false;
15127       }
15128       return true;
15129     }
15130 
15131     static bool _is_CImg3d(const T val, const char c) {
15132       return val>=(T)c && val<(T)(c + 1);
15133     }
15134 
15135     //@}
15136     //-------------------------------------
15137     //
15138     //! \name Mathematical Functions
15139     //@{
15140     //-------------------------------------
15141 
15142     // Define the math formula parser/compiler and expression evaluator.
15143     struct _cimg_math_parser {
15144       CImg<doubleT> mem;
15145       CImg<intT> memtype;
15146       CImgList<ulongT> _code, &code, code_init, code_end;
15147       CImg<ulongT> opcode;
15148       const CImg<ulongT> *p_code_end, *p_code;
15149       const CImg<ulongT> *const p_break;
15150 
15151       CImg<charT> expr, pexpr;
15152       const CImg<T>& imgin;
15153       const CImgList<T>& listin;
15154       CImg<T> &imgout;
15155       CImgList<T>& listout;
15156 
15157       CImg<doubleT> _img_stats, &img_stats, constcache_vals;
15158       CImgList<doubleT> _list_stats, &list_stats, _list_median, &list_median;
15159       CImg<uintT> mem_img_stats, constcache_inds;
15160 
15161       CImg<uintT> level, variable_pos, reserved_label;
15162       CImgList<charT> variable_def, macro_def, macro_body;
15163       CImgList<boolT> macro_body_is_string;
15164       char *user_macro;
15165 
15166       unsigned int mempos, mem_img_median, debug_indent, result_dim, break_type, constcache_size;
15167       bool is_parallelizable, is_fill, need_input_copy;
15168       double *result;
15169       const char *const calling_function, *s_op, *ss_op;
15170       typedef double (*mp_func)(_cimg_math_parser&);
15171 
15172 #define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value?
15173 #define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value?
15174 #define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value?
15175 #define _cimg_mp_is_variable(arg) (memtype[arg]==-1) // Is scalar variable?
15176 #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector?
15177 #define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN)
15178 #define _cimg_mp_calling_function calling_function_s()._data
15179 #define _cimg_mp_op(s) s_op = s; ss_op = ss
15180 #define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char)
15181 #define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char)
15182 #define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char)
15183 #define _cimg_mp_check_vector0(dim) check_vector0(dim,ss,se,saved_char)
15184 #define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char)
15185 #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp)
15186 #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; }
15187 #define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan)
15188 #define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val)))
15189 #define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op))
15190 #define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1))
15191 #define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2))
15192 #define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3))
15193 #define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4))
15194 #define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5))
15195 #define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6))
15196 #define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7))
15197 #define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1))
15198 #define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2))
15199 #define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2))
15200 #define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2))
15201 #define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3))
15202 
15203       // Constructors.
15204       _cimg_math_parser(const char *const expression, const char *const funcname=0,
15205                         const CImg<T>& img_input=CImg<T>::const_empty(), CImg<T> *const img_output=0,
15206                         const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0,
15207                         const bool _is_fill=false):
15208         code(_code),p_break((CImg<ulongT>*)0 - 2),
15209         imgin(img_input),listin(list_inputs?*list_inputs:CImgList<T>::const_empty()),
15210         imgout(img_output?*img_output:CImg<T>::empty()),listout(list_outputs?*list_outputs:CImgList<T>::empty()),
15211         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),user_macro(0),
15212         mem_img_median(~0U),debug_indent(0),result_dim(0),break_type(0),constcache_size(0),
15213         is_parallelizable(true),is_fill(_is_fill),need_input_copy(false),
15214         calling_function(funcname?funcname:"cimg_math_parser") {
15215         if (!expression || !*expression)
15216           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15217                                       "CImg<%s>::%s: Empty expression.",
15218                                       pixel_type(),_cimg_mp_calling_function);
15219         const char *_expression = expression;
15220         while (*_expression && ((signed char)*_expression<=' ' || *_expression==';')) ++_expression;
15221         CImg<charT>::string(_expression).move_to(expr);
15222         char *ps = &expr.back() - 1;
15223         while (ps>expr._data && ((signed char)*ps<=' ' || *ps==';')) --ps;
15224         *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1);
15225 
15226         // Ease the retrieval of previous non-space characters afterwards.
15227         pexpr.assign(expr._width);
15228         char c, *pe = pexpr._data;
15229         for (ps = expr._data, c = ' '; *ps; ++ps) {
15230           if ((signed char)*ps>' ') c = *ps; else *ps = ' ';
15231           *(pe++) = c;
15232         }
15233         *pe = 0;
15234         level = get_level(expr);
15235 
15236         // Init constant values.
15237 #define _cimg_mp_interpolation (reserved_label[29]!=~0U?reserved_label[29]:0)
15238 #define _cimg_mp_boundary (reserved_label[30]!=~0U?reserved_label[30]:0)
15239 #define _cimg_mp_slot_nan 29
15240 #define _cimg_mp_slot_x 30
15241 #define _cimg_mp_slot_y 31
15242 #define _cimg_mp_slot_z 32
15243 #define _cimg_mp_slot_c 33
15244 
15245         mem.assign(96);
15246         for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10
15247         for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5
15248         mem[16] = 0.5;
15249         mem[17] = 0; // thread_id
15250         mem[18] = (double)imgin._width; // w
15251         mem[19] = (double)imgin._height; // h
15252         mem[20] = (double)imgin._depth; // d
15253         mem[21] = (double)imgin._spectrum; // s
15254         mem[22] = (double)imgin._is_shared; // r
15255         mem[23] = (double)imgin._width*imgin._height; // wh
15256         mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd
15257         mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds
15258         mem[26] = (double)listin._width; // l
15259         mem[27] = std::exp(1.0); // e
15260         mem[28] = cimg::PI; // pi
15261         mem[_cimg_mp_slot_nan] = cimg::type<double>::nan(); // nan
15262 
15263         // Set value property :
15264         // { -2 = other | -1 = variable | 0 = computation value |
15265         //    1 = compile-time constant | N>1 = constant ptr to vector[N-1] }.
15266         memtype.assign(mem._width,1,1,1,0);
15267         for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1;
15268         memtype[17] = 0;
15269         memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] = memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -2;
15270         mempos = _cimg_mp_slot_c + 1;
15271         variable_pos.assign(8);
15272 
15273         reserved_label.assign(128,1,1,1,~0U);
15274         // reserved_label[4-28] are used to store these two-char variables:
15275         // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv,
15276         // [8] = is, [9] = ip, [10] = ic, [11] = xm, [12] = ym, [13] = zm, [14] = cm, [15] = xM,
15277         // [16] = yM, [17] = zM, [18]=cM, [19]=i0...[28]=i9, [29] = interpolation, [30] = boundary
15278 
15279         // Compile expression into a serie of opcodes.
15280         s_op = ""; ss_op = expr._data;
15281         const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false);
15282         if (!_cimg_mp_is_constant(ind_result)) {
15283           if (_cimg_mp_is_vector(ind_result))
15284             CImg<doubleT>(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true).
15285               fill(cimg::type<double>::nan());
15286           else mem[ind_result] = cimg::type<double>::nan();
15287         }
15288 
15289         // Free resources used for compiling expression and prepare evaluation.
15290         result_dim = _cimg_mp_size(ind_result);
15291         if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1);
15292         result = mem._data + ind_result;
15293         memtype.assign();
15294         constcache_vals.assign();
15295         constcache_inds.assign();
15296         level.assign();
15297         variable_pos.assign();
15298         reserved_label.assign();
15299         expr.assign();
15300         pexpr.assign();
15301         opcode.assign();
15302         opcode._is_shared = true;
15303 
15304         // Execute init() bloc if any specified.
15305         if (code_init) {
15306           mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
15307           p_code_end = code_init.end();
15308           for (p_code = code_init; p_code<p_code_end; ++p_code) {
15309             opcode._data = p_code->_data;
15310             const ulongT target = opcode[1];
15311             mem[target] = _cimg_mp_defunc(*this);
15312           }
15313         }
15314         p_code_end = code.end();
15315       }
15316 
15317       _cimg_math_parser():
15318         code(_code),p_code_end(0),p_break((CImg<ulongT>*)0 - 2),
15319         imgin(CImg<T>::const_empty()),listin(CImgList<T>::const_empty()),
15320         imgout(CImg<T>::empty()),listout(CImgList<T>::empty()),
15321         img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),debug_indent(0),
15322         result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false),need_input_copy(false),
15323         calling_function(0) {
15324         mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()()
15325         result = mem._data;
15326       }
15327 
15328       _cimg_math_parser(const _cimg_math_parser& mp):
15329         mem(mp.mem),code(mp.code),p_code_end(mp.p_code_end),p_break(mp.p_break),
15330         imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout),img_stats(mp.img_stats),
15331         list_stats(mp.list_stats),list_median(mp.list_median),debug_indent(0),result_dim(mp.result_dim),
15332         break_type(0),constcache_size(0),is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill),
15333         need_input_copy(mp.need_input_copy), result(mem._data + (mp.result - mp.mem._data)),calling_function(0) {
15334 #ifdef cimg_use_openmp
15335         mem[17] = omp_get_thread_num();
15336 #endif
15337         opcode.assign();
15338         opcode._is_shared = true;
15339       }
15340 
15341       // Count parentheses/brackets level of each character of the expression.
15342       CImg<uintT> get_level(CImg<charT>& expr) const {
15343         bool is_escaped = false, next_is_escaped = false;
15344         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
15345         CImg<uintT> res(expr._width - 1);
15346         unsigned int *pd = res._data;
15347         int level = 0;
15348         for (const char *ps = expr._data; *ps && level>=0; ++ps) {
15349           if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true;
15350           if (!is_escaped && *ps=='\'') { // Non-escaped character
15351             if (!mode && ps>expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
15352             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
15353             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
15354           }
15355           *(pd++) = (unsigned int)(mode>=1 || is_escaped?level + (mode==1):
15356                                    *ps=='(' || *ps=='['?level++:
15357                                    *ps==')' || *ps==']'?--level:
15358                                    level);
15359           mode = next_mode;
15360           is_escaped = next_is_escaped;
15361           next_is_escaped = false;
15362         }
15363         if (mode) {
15364           cimg::strellipsize(expr,64);
15365           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15366                                       "CImg<%s>::%s: Unterminated string literal, in expression '%s'.",
15367                                       pixel_type(),_cimg_mp_calling_function,
15368                                       expr._data);
15369         }
15370         if (level) {
15371           cimg::strellipsize(expr,64);
15372           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15373                                       "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.",
15374                                       pixel_type(),_cimg_mp_calling_function,
15375                                       expr._data);
15376         }
15377         return res;
15378       }
15379 
15380       // Tell for each character of an expression if it is inside a string or not.
15381       CImg<boolT> is_inside_string(CImg<charT>& expr) const {
15382         bool is_escaped = false, next_is_escaped = false;
15383         unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
15384         CImg<boolT> res = CImg<charT>::string(expr);
15385         bool *pd = res._data;
15386         for (const char *ps = expr._data; *ps; ++ps) {
15387           if (!next_is_escaped && *ps=='\\') next_is_escaped = true;
15388           if (!is_escaped && *ps=='\'') { // Non-escaped character
15389             if (!mode && ps>expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
15390             else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
15391             else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
15392           }
15393           *(pd++) = mode>=1 || is_escaped;
15394           mode = next_mode;
15395           is_escaped = next_is_escaped;
15396           next_is_escaped = false;
15397         }
15398         return res;
15399       }
15400 
15401       // Compilation procedure.
15402       unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref,
15403                            const bool is_single) {
15404         if (depth>256) {
15405           cimg::strellipsize(expr,64);
15406           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15407                                       "CImg<%s>::%s: Call stack overflow (infinite recursion?), "
15408                                       "in expression '%s%s%s'.",
15409                                       pixel_type(),_cimg_mp_calling_function,
15410                                       (ss - 4)>expr._data?"...":"",
15411                                       (ss - 4)>expr._data?ss - 4:expr._data,
15412                                       se<&expr.back()?"...":"");
15413         }
15414         char c1, c2, c3, c4;
15415 
15416         // Simplify expression when possible.
15417         do {
15418           c2 = 0;
15419           if (ss<se) {
15420             while (*ss && ((signed char)*ss<=' ' || *ss==';')) ++ss;
15421             while (se>ss && ((signed char)(c1 = *(se - 1))<=' ' || c1==';')) --se;
15422           }
15423           while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) {
15424             ++ss; --se; c2 = 1;
15425           }
15426         } while (c2 && ss<se);
15427 
15428         if (se<=ss || !*ss) {
15429           cimg::strellipsize(expr,64);
15430           throw CImgArgumentException("[" cimg_appname "_math_parser] "
15431                                       "CImg<%s>::%s: %s%s Missing %s, in expression '%s%s%s'.",
15432                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
15433                                       *s_op=='F'?"argument":"item",
15434                                       (ss_op - 4)>expr._data?"...":"",
15435                                       (ss_op - 4)>expr._data?ss_op - 4:expr._data,
15436                                       ss_op + std::strlen(ss_op)<&expr.back()?"...":"");
15437         }
15438 
15439         const char *const previous_s_op = s_op, *const previous_ss_op = ss_op;
15440         const unsigned int depth1 = depth + 1;
15441         unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6;
15442         char
15443           *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3,
15444           *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4,
15445           *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8,
15446           *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0;
15447         double val = 0, val1, val2;
15448         mp_func op;
15449 
15450         // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value
15451         // linked to the returned memory slot (reference that cannot be determined at compile time).
15452         // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) |
15453         //                   3 = image value (coordinates) | 4 = image value as a vector (offsets) |
15454         //                   5 = image value as a vector (coordinates) }.
15455         // Depending on p_ref[0], the remaining p_ref[k] have the following meaning:
15456         // When p_ref[0]==0, p_ref is actually unlinked.
15457         // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ].
15458         // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ].
15459         // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ].
15460         // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ].
15461         // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ].
15462         if (p_ref) { *p_ref = 0; p_ref[1] = p_ref[2] = p_ref[3] = p_ref[4] = p_ref[5] = p_ref[6] = ~0U; }
15463 
15464         const char saved_char = *se; *se = 0;
15465         const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1;
15466         bool is_sth, is_relative;
15467         CImg<uintT> ref;
15468         CImg<charT> variable_name;
15469         CImgList<ulongT> l_opcode;
15470 
15471         // Look for a single value or a pre-defined variable.
15472         int nb = 0;
15473         s = ss + (*ss=='+' || *ss=='-'?1:0);
15474         if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf
15475           is_sth = !(*ss=='-');
15476           if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; }
15477           else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; }
15478           if (nb==1 && !is_sth) val = -val;
15479         }
15480         if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0));
15481         if (nb==1) _cimg_mp_constant(val);
15482         if (nb==2 && sep=='%') _cimg_mp_constant(val/100);
15483 
15484         if (ss1==se) switch (*ss) { // One-char reserved variable
15485           case 'c' : _cimg_mp_return(reserved_label['c']!=~0U?reserved_label['c']:_cimg_mp_slot_c);
15486           case 'd' : _cimg_mp_return(reserved_label['d']!=~0U?reserved_label['d']:20);
15487           case 'e' : _cimg_mp_return(reserved_label['e']!=~0U?reserved_label['e']:27);
15488           case 'h' : _cimg_mp_return(reserved_label['h']!=~0U?reserved_label['h']:19);
15489           case 'l' : _cimg_mp_return(reserved_label['l']!=~0U?reserved_label['l']:26);
15490           case 'r' : _cimg_mp_return(reserved_label['r']!=~0U?reserved_label['r']:22);
15491           case 's' : _cimg_mp_return(reserved_label['s']!=~0U?reserved_label['s']:21);
15492           case 't' : _cimg_mp_return(reserved_label['t']!=~0U?reserved_label['t']:17);
15493           case 'w' : _cimg_mp_return(reserved_label['w']!=~0U?reserved_label['w']:18);
15494           case 'x' : _cimg_mp_return(reserved_label['x']!=~0U?reserved_label['x']:_cimg_mp_slot_x);
15495           case 'y' : _cimg_mp_return(reserved_label['y']!=~0U?reserved_label['y']:_cimg_mp_slot_y);
15496           case 'z' : _cimg_mp_return(reserved_label['z']!=~0U?reserved_label['z']:_cimg_mp_slot_z);
15497           case 'u' :
15498             if (reserved_label['u']!=~0U) _cimg_mp_return(reserved_label['u']);
15499             _cimg_mp_scalar2(mp_u,0,1);
15500           case 'g' :
15501             if (reserved_label['g']!=~0U) _cimg_mp_return(reserved_label['g']);
15502             _cimg_mp_scalar0(mp_g);
15503           case 'i' :
15504             if (reserved_label['i']!=~0U) _cimg_mp_return(reserved_label['i']);
15505             _cimg_mp_scalar0(mp_i);
15506           case 'I' :
15507             _cimg_mp_op("Variable 'I'");
15508             if (reserved_label['I']!=~0U) _cimg_mp_return(reserved_label['I']);
15509             _cimg_mp_check_vector0(imgin._spectrum);
15510             need_input_copy = true;
15511             pos = vector(imgin._spectrum);
15512             CImg<ulongT>::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code);
15513             _cimg_mp_return(pos);
15514           case 'R' :
15515             if (reserved_label['R']!=~0U) _cimg_mp_return(reserved_label['R']);
15516             need_input_copy = true;
15517             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0);
15518           case 'G' :
15519             if (reserved_label['G']!=~0U) _cimg_mp_return(reserved_label['G']);
15520             need_input_copy = true;
15521             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0);
15522           case 'B' :
15523             if (reserved_label['B']!=~0U) _cimg_mp_return(reserved_label['B']);
15524             need_input_copy = true;
15525             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0);
15526           case 'A' :
15527             if (reserved_label['A']!=~0U) _cimg_mp_return(reserved_label['A']);
15528             need_input_copy = true;
15529             _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0);
15530           }
15531         else if (ss2==se) { // Two-chars reserved variable
15532           arg1 = arg2 = ~0U;
15533           if (*ss=='w' && *ss1=='h') // wh
15534             _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23);
15535           if (*ss=='p' && *ss1=='i') // pi
15536             _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28);
15537           if (*ss=='i') {
15538             if (*ss1>='0' && *ss1<='9') { // i0...i9
15539               pos = 19 + *ss1 - '0';
15540               if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]);
15541               need_input_copy = true;
15542               _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 19,0,0);
15543             }
15544             switch (*ss1) {
15545             case 'm' : arg1 = 4; arg2 = 0; break; // im
15546             case 'M' : arg1 = 5; arg2 = 1; break; // iM
15547             case 'a' : arg1 = 6; arg2 = 2; break; // ia
15548             case 'v' : arg1 = 7; arg2 = 3; break; // iv
15549             case 's' : arg1 = 8; arg2 = 12; break; // is
15550             case 'p' : arg1 = 9; arg2 = 13; break; // ip
15551             case 'c' : // ic
15552               if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]);
15553               if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0;
15554               _cimg_mp_return(mem_img_median);
15555               break;
15556             }
15557           }
15558           else if (*ss1=='m') switch (*ss) {
15559             case 'x' : arg1 = 11; arg2 = 4; break; // xm
15560             case 'y' : arg1 = 12; arg2 = 5; break; // ym
15561             case 'z' : arg1 = 13; arg2 = 6; break; // zm
15562             case 'c' : arg1 = 14; arg2 = 7; break; // cm
15563             }
15564           else if (*ss1=='M') switch (*ss) {
15565             case 'x' : arg1 = 15; arg2 = 8; break; // xM
15566             case 'y' : arg1 = 16; arg2 = 9; break; // yM
15567             case 'z' : arg1 = 17; arg2 = 10; break; // zM
15568             case 'c' : arg1 = 18; arg2 = 11; break; // cM
15569             }
15570           if (arg1!=~0U) {
15571             if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]);
15572             if (!img_stats) {
15573               img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false);
15574               mem_img_stats.assign(1,14,1,1,~0U);
15575             }
15576             if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]);
15577             _cimg_mp_return(mem_img_stats[arg2]);
15578           }
15579         } else if (ss3==se) { // Three-chars reserved variable
15580           if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd
15581             _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24);
15582         } else if (ss4==se) { // Four-chars reserved variable
15583           if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds
15584             _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25);
15585         }
15586 
15587         pos = ~0U;
15588         is_sth = false;
15589         for (s0 = ss, s = ss1; s<se1; ++s)
15590           if (*s==';' && level[s - expr._data]==clevel) { // Separator ';'
15591             arg1 = code_end._width;
15592             arg2 = compile(s0,s++,depth,0,is_single);
15593             if (code_end._width==arg1) pos = arg2; // makes 'end()' return void
15594             is_sth = true;
15595             while (*s && ((signed char)*s<=' ' || *s==';')) ++s;
15596             s0 = s;
15597           }
15598         if (is_sth) {
15599           arg1 = code_end._width;
15600           arg2 = compile(s0,se,depth,p_ref,is_single);
15601           if (code_end._width==arg1) pos = arg2; // makes 'end()' return void
15602           _cimg_mp_return(pos);
15603         }
15604 
15605         // Declare / assign variable, vector value or image value.
15606         for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns)
15607           if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' &&
15608               *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' &&
15609               *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' &&
15610               level[s - expr._data]==clevel) {
15611             variable_name.assign(ss,(unsigned int)(s + 1 - ss)).back() = 0;
15612             cimg::strpare(variable_name,false,true);
15613             const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name);
15614             char *const ve1 = ss + l_variable_name - 1;
15615             _cimg_mp_op("Operator '='");
15616 
15617             // Assign image value (direct).
15618             if (l_variable_name>2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') &&
15619                 (reserved_label[*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[*ss]))) {
15620               is_relative = *ss=='j' || *ss=='J';
15621 
15622               if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value
15623                 if (!is_single) is_parallelizable = false;
15624                 if (*ss2=='#') { // Index specified
15625                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
15626                   p1 = compile(ss3,s0++,depth1,0,is_single);
15627                   _cimg_mp_check_list(true);
15628                 } else { p1 = ~0U; s0 = ss2; }
15629                 arg1 = compile(s0,ve1,depth1,0,is_single); // Offset
15630                 _cimg_mp_check_type(arg1,0,1,0);
15631                 arg2 = compile(s + 1,se,depth1,0,is_single); // Value to assign
15632                 if (_cimg_mp_is_vector(arg2)) {
15633                   p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
15634                   if (p1==~0U) p2 = imgin._spectrum;
15635                   else if (_cimg_mp_is_constant(p1)) {
15636                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
15637                     p2 = listin[p3]._spectrum;
15638                   }
15639                   _cimg_mp_check_vector0(p2);
15640                 } else p2 = 0;
15641                 _cimg_mp_check_type(arg2,2,*ss>='i'?1:3,p2);
15642 
15643                 if (p_ref) {
15644                   *p_ref = _cimg_mp_is_vector(arg2)?4:2;
15645                   p_ref[1] = p1;
15646                   p_ref[2] = (unsigned int)is_relative;
15647                   p_ref[3] = arg1;
15648                   if (_cimg_mp_is_vector(arg2))
15649                     set_variable_vector(arg2); // Prevent from being used in further optimization
15650                   else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
15651                   if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2;
15652                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
15653                 }
15654 
15655 
15656                 if (p1!=~0U) {
15657                   if (!listout) _cimg_mp_return(arg2);
15658                   if (*ss>='i')
15659                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
15660                                         arg2,p1,arg1).move_to(code);
15661                   else if (_cimg_mp_is_scalar(arg2))
15662                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
15663                                         arg2,p1,arg1).move_to(code);
15664                   else
15665                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
15666                                         arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code);
15667                 } else {
15668                   if (!imgout) _cimg_mp_return(arg2);
15669                   if (*ss>='i')
15670                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
15671                                         arg2,arg1).move_to(code);
15672                   else if (_cimg_mp_is_scalar(arg2))
15673                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
15674                                         arg2,arg1).move_to(code);
15675                   else
15676                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
15677                                         arg2,arg1,_cimg_mp_size(arg2)).move_to(code);
15678                 }
15679                 _cimg_mp_return(arg2);
15680               }
15681 
15682               if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value
15683                 if (!is_single) is_parallelizable = false;
15684                 if (*ss2=='#') { // Index specified
15685                   s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
15686                   p1 = compile(ss3,s0++,depth1,0,is_single);
15687                   _cimg_mp_check_list(true);
15688                 } else { p1 = ~0U; s0 = ss2; }
15689                 arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
15690                 arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
15691                 arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
15692                 arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
15693                 arg5 = compile(s + 1,se,depth1,0,is_single); // Value to assign
15694                 if (s0<ve1) { // X or [ X,_Y,_Z,_C ]
15695                   s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
15696                   arg1 = compile(s0,s1,depth1,0,is_single);
15697                   if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
15698                     p2 = _cimg_mp_size(arg1); // Vector size
15699                     ++arg1;
15700                     if (p2>1) {
15701                       arg2 = arg1 + 1;
15702                       if (p2>2) {
15703                         arg3 = arg2 + 1;
15704                         if (p2>3) arg4 = arg3 + 1;
15705                       }
15706                     }
15707                   } else if (s1<ve1) { // Y
15708                     s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
15709                     arg2 = compile(s1,s2,depth1,0,is_single);
15710                     if (s2<ve1) { // Z
15711                       s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
15712                       arg3 = compile(s2,s3,depth1,0,is_single);
15713                       if (s3<ve1) arg4 = compile(++s3,ve1,depth1,0,is_single); // C
15714                     }
15715                   }
15716                 }
15717 
15718                 if (_cimg_mp_is_vector(arg5)) {
15719                   p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
15720                   if (p1==~0U) p2 = imgin._spectrum;
15721                   else if (_cimg_mp_is_constant(p1)) {
15722                     p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
15723                     p2 = listin[p3]._spectrum;
15724                   }
15725                   _cimg_mp_check_vector0(p2);
15726                 } else p2 = 0;
15727                 _cimg_mp_check_type(arg5,2,*ss>='i'?1:3,p2);
15728 
15729                 if (p_ref) {
15730                   *p_ref = _cimg_mp_is_vector(arg5)?5:3;
15731                   p_ref[1] = p1;
15732                   p_ref[2] = (unsigned int)is_relative;
15733                   p_ref[3] = arg1;
15734                   p_ref[4] = arg2;
15735                   p_ref[5] = arg3;
15736                   p_ref[6] = arg4;
15737                   if (_cimg_mp_is_vector(arg5))
15738                     set_variable_vector(arg5); // Prevent from being used in further optimization
15739                   else if (_cimg_mp_is_comp(arg5)) memtype[arg5] = -2;
15740                   if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2;
15741                   if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
15742                   if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
15743                   if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
15744                   if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2;
15745                 }
15746                 if (p1!=~0U) {
15747                   if (!listout) _cimg_mp_return(arg5);
15748                   if (*ss>='i')
15749                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
15750                                         arg5,p1,arg1,arg2,arg3,arg4).move_to(code);
15751                   else if (_cimg_mp_is_scalar(arg5))
15752                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
15753                                         arg5,p1,arg1,arg2,arg3).move_to(code);
15754                   else
15755                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
15756                                         arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
15757                 } else {
15758                   if (!imgout) _cimg_mp_return(arg5);
15759                   if (*ss>='i')
15760                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
15761                                         arg5,arg1,arg2,arg3,arg4).move_to(code);
15762                   else if (_cimg_mp_is_scalar(arg5))
15763                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
15764                                         arg5,arg1,arg2,arg3).move_to(code);
15765                   else
15766                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
15767                                         arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
15768                 }
15769                 _cimg_mp_return(arg5);
15770               }
15771             }
15772 
15773             // Assign vector value (direct).
15774             if (l_variable_name>3 && *ve1==']' && *ss!='[') {
15775               s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
15776               is_sth = true; // is_valid_variable_name?
15777               if (*ss>='0' && *ss<='9') is_sth = false;
15778               else for (ns = ss; ns<s0; ++ns)
15779                      if (!is_varchar(*ns)) { is_sth = false; break; }
15780               if (is_sth && s0>ss) {
15781                 variable_name[s0 - ss] = 0; // Remove brackets in variable name
15782                 arg1 = ~0U; // Vector slot
15783                 arg2 = compile(++s0,ve1,depth1,0,is_single); // Index
15784                 arg3 = compile(s + 1,se,depth1,0,is_single); // Value to assign
15785                 _cimg_mp_check_type(arg3,2,1,0);
15786 
15787                 if (variable_name[1]) { // Multi-char variable
15788                   cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i])) {
15789                     arg1 = variable_pos[i]; break;
15790                   }
15791                 } else arg1 = reserved_label[*variable_name]; // Single-char variable
15792                 if (arg1==~0U) compile(ss,s0 - 1,depth1,0,is_single); // Variable does not exist -> error
15793                 else { // Variable already exists
15794                   if (_cimg_mp_is_scalar(arg1)) compile(ss,s,depth1,0,is_single); // Variable is not a vector -> error
15795                   if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly
15796                     nb = (int)mem[arg2];
15797                     if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) {
15798                       arg1+=nb + 1;
15799                       CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
15800                       _cimg_mp_return(arg1);
15801                     }
15802                     compile(ss,s,depth1,0,is_single); // Out-of-bounds reference -> error
15803                   }
15804 
15805                   // Case of non-constant index -> return assigned value + linked reference
15806                   if (p_ref) {
15807                     *p_ref = 1;
15808                     p_ref[1] = arg1;
15809                     p_ref[2] = arg2;
15810                     if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2; // Prevent from being used in further optimization
15811                     if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
15812                   }
15813                   CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),
15814                                        arg2,arg3).
15815                     move_to(code);
15816                   _cimg_mp_return(arg3);
15817                 }
15818               }
15819             }
15820 
15821             // Assign user-defined macro.
15822             if (l_variable_name>2 && *ve1==')' && *ss!='(') {
15823               s0 = ve1; while (s0>ss && *s0!='(') --s0;
15824               is_sth = std::strncmp(variable_name,"debug(",6) &&
15825                 std::strncmp(variable_name,"print(",6); // is_valid_function_name?
15826               if (*ss>='0' && *ss<='9') is_sth = false;
15827               else for (ns = ss; ns<s0; ++ns)
15828                      if (!is_varchar(*ns)) { is_sth = false; break; }
15829 
15830               if (is_sth && s0>ss) { // Looks like a valid function declaration
15831                 s0 = variable_name._data + (s0 - ss);
15832                 *s0 = 0;
15833                 s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis
15834                 CImg<charT>(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0);
15835                 ++s; while (*s && (signed char)*s<=' ') ++s;
15836                 CImg<charT>(s,(unsigned int)(se - s + 1)).move_to(macro_body,0);
15837 
15838                 p1 = 1; // Indice of current parsed argument
15839                 for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments
15840                   if (p1>24) {
15841                     *se = saved_char;
15842                     cimg::strellipsize(variable_name,64);
15843                     s0 = ss - 4>expr._data?ss - 4:expr._data;
15844                     cimg::strellipsize(s0,64);
15845                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
15846                                                 "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro "
15847                                                 "definition '%s()', in expression '%s%s%s'.",
15848                                                 pixel_type(),_cimg_mp_calling_function,s_op,
15849                                                 variable_name._data,
15850                                                 s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
15851                   }
15852                   while (*s && (signed char)*s<=' ') ++s;
15853                   if (*s==')' && p1==1) break; // Function has no arguments
15854 
15855                   s2 = s; // Start of the argument name
15856                   is_sth = true; // is_valid_argument_name?
15857                   if (*s>='0' && *s<='9') is_sth = false;
15858                   else for (ns = s; ns<s1 && *ns!=',' && (signed char)*ns>' '; ++ns)
15859                          if (!is_varchar(*ns)) { is_sth = false; break; }
15860                   s3 = ns; // End of the argument name
15861                   while (*ns && (signed char)*ns<=' ') ++ns;
15862                   if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) {
15863                     *se = saved_char;
15864                     cimg::strellipsize(variable_name,64);
15865                     s0 = ss - 4>expr._data?ss - 4:expr._data;
15866                     cimg::strellipsize(s0,64);
15867                     throw CImgArgumentException("[" cimg_appname "_math_parser] "
15868                                                 "CImg<%s>::%s: %s: %s name specified for argument %u when defining "
15869                                                 "macro '%s()', in expression '%s%s%s'.",
15870                                                 pixel_type(),_cimg_mp_calling_function,s_op,
15871                                                 is_sth?"Empty":"Invalid",p1,
15872                                                 variable_name._data,
15873                                                 s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
15874                   }
15875                   if (ns==s1 || *ns==',') { // New argument found
15876                     *s3 = 0;
15877                     p2 = (unsigned int)(s3 - s2); // Argument length
15878                     for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number
15879                       if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) ||
15880                             (ps + p2<macro_body[0].end() && is_varchar(*(ps + p2))))) {
15881                         if (ps>macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign
15882                           *(ps - 1) = (char)p1;
15883                           if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Has pre & post number signs
15884                             std::memmove(ps,ps + p2 + 1,macro_body[0].end() - ps - p2 - 1);
15885                             macro_body[0]._width-=p2 + 1;
15886                           } else { // Has pre number sign only
15887                             std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
15888                             macro_body[0]._width-=p2;
15889                           }
15890                         } else if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Remove post-number sign
15891                           *(ps++) = (char)p1;
15892                           std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
15893                           macro_body[0]._width-=p2;
15894                         } else { // Not near a number sign
15895                           if (p2<3) {
15896                             ps-=(ulongT)macro_body[0]._data;
15897                             macro_body[0].resize(macro_body[0]._width - p2 + 3,1,1,1,0);
15898                             ps+=(ulongT)macro_body[0]._data;
15899                           } else macro_body[0]._width-=p2 - 3;
15900                           std::memmove(ps + 3,ps + p2,macro_body[0].end() - ps - 3);
15901                           *(ps++) = '(';
15902                           *(ps++) = (char)p1;
15903                           *(ps++) = ')';
15904                         }
15905                       } else ++ps;
15906                     }
15907                   }
15908                 }
15909 
15910                 // Store number of arguments.
15911                 macro_def[0].resize(macro_def[0]._width + 1,1,1,1,0).back() = (char)(p1 - 1);
15912 
15913                 // Detect parts of function body inside a string.
15914                 is_inside_string(macro_body[0]).move_to(macro_body_is_string,0);
15915                 _cimg_mp_return_nan();
15916               }
15917             }
15918 
15919             // Check if the variable name could be valid. If not, this is probably an lvalue assignment.
15920             is_sth = true; // is_valid_variable_name?
15921             const bool is_const = l_variable_name>6 && !std::strncmp(variable_name,"const ",6);
15922 
15923             s0 = variable_name._data;
15924             if (is_const) {
15925               s0+=6; while ((signed char)*s0<=' ') ++s0;
15926               variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1);
15927             }
15928 
15929             if (*variable_name>='0' && *variable_name<='9') is_sth = false;
15930             else for (ns = variable_name._data; *ns; ++ns)
15931                    if (!is_varchar(*ns)) { is_sth = false; break; }
15932 
15933             // Assign variable (direct).
15934             if (is_sth) {
15935               arg3 = variable_name[1]?~0U:*variable_name; // One-char variable
15936               if (variable_name[1] && !variable_name[2]) { // Two-chars variable
15937                 c1 = variable_name[0];
15938                 c2 = variable_name[1];
15939                 if (c1=='w' && c2=='h') arg3 = 0; // wh
15940                 else if (c1=='p' && c2=='i') arg3 = 3; // pi
15941                 else if (c1=='i') {
15942                   if (c2>='0' && c2<='9') arg3 = 19 + c2 - '0'; // i0...i9
15943                   else if (c2=='m') arg3 = 4; // im
15944                   else if (c2=='M') arg3 = 5; // iM
15945                   else if (c2=='a') arg3 = 6; // ia
15946                   else if (c2=='v') arg3 = 7; // iv
15947                   else if (c2=='s') arg3 = 8; // is
15948                   else if (c2=='p') arg3 = 9; // ip
15949                   else if (c2=='c') arg3 = 10; // ic
15950                 } else if (c2=='m') {
15951                   if (c1=='x') arg3 = 11; // xm
15952                   else if (c1=='y') arg3 = 12; // ym
15953                   else if (c1=='z') arg3 = 13; // zm
15954                   else if (c1=='c') arg3 = 14; // cm
15955                 } else if (c2=='M') {
15956                   if (c1=='x') arg3 = 15; // xM
15957                   else if (c1=='y') arg3 = 16; // yM
15958                   else if (c1=='z') arg3 = 17; // zM
15959                   else if (c1=='c') arg3 = 18; // cM
15960                 }
15961               } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable
15962                 c1 = variable_name[0];
15963                 c2 = variable_name[1];
15964                 c3 = variable_name[2];
15965                 if (c1=='w' && c2=='h' && c3=='d') arg3 = 1; // whd
15966               } else if (variable_name[1] && variable_name[2] && variable_name[3] &&
15967                          !variable_name[4]) { // Four-chars variable
15968                 c1 = variable_name[0];
15969                 c2 = variable_name[1];
15970                 c3 = variable_name[2];
15971                 c4 = variable_name[3];
15972                 if (c1=='w' && c2=='h' && c3=='d' && c4=='s') arg3 = 2; // whds
15973               } else if (!std::strcmp(variable_name,"interpolation")) arg3 = 29; // interpolation
15974               else if (!std::strcmp(variable_name,"boundary")) arg3 = 30; // boundary
15975 
15976               arg1 = ~0U;
15977               arg2 = compile(s + 1,se,depth1,0,is_single);
15978               if (is_const) _cimg_mp_check_constant(arg2,2,0);
15979 
15980               if (arg3!=~0U) // One-char variable, or variable in reserved_labels
15981                 arg1 = reserved_label[arg3];
15982               else // Multi-char variable name : check for existing variable with same name
15983                 cimglist_for(variable_def,i)
15984                   if (!std::strcmp(variable_name,variable_def[i])) { arg1 = variable_pos[i]; break; }
15985 
15986               if (arg1==~0U) { // Create new variable
15987                 if (_cimg_mp_is_vector(arg2)) { // Vector variable
15988                   arg1 = is_comp_vector(arg2)?arg2:vector_copy(arg2);
15989                   set_variable_vector(arg1);
15990                 } else { // Scalar variable
15991                   if (is_const) arg1 = arg2;
15992                   else {
15993                     arg1 = _cimg_mp_is_comp(arg2)?arg2:scalar1(mp_copy,arg2);
15994                     memtype[arg1] = -1;
15995                   }
15996                 }
15997 
15998                 if (arg3!=~0U) reserved_label[arg3] = arg1;
15999                 else {
16000                   if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
16001                   variable_pos[variable_def._width] = arg1;
16002                   variable_name.move_to(variable_def);
16003                 }
16004 
16005               } else { // Variable already exists -> assign a new value
16006                 if (is_const || _cimg_mp_is_constant(arg1)) {
16007                   *se = saved_char;
16008                   cimg::strellipsize(variable_name,64);
16009                   s0 = ss - 4>expr._data?ss - 4:expr._data;
16010                   cimg::strellipsize(s0,64);
16011                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
16012                                               "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, "
16013                                               "in expression '%s%s%s'.",
16014                                               pixel_type(),_cimg_mp_calling_function,s_op,
16015                                               _cimg_mp_is_constant(arg1)?"already-defined ":"non-",
16016                                               variable_name._data,
16017                                               !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"",
16018                                               s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
16019                 }
16020                 _cimg_mp_check_type(arg2,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1));
16021                 if (_cimg_mp_is_vector(arg1)) { // Vector
16022                   if (_cimg_mp_is_vector(arg2)) // From vector
16023                     CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
16024                       move_to(code);
16025                   else // From scalar
16026                     CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
16027                       move_to(code);
16028                 } else // Scalar
16029                   CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
16030               }
16031               _cimg_mp_return(arg1);
16032             }
16033 
16034             // Assign lvalue (variable name was not valid for a direct assignment).
16035             arg1 = ~0U;
16036             is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator?
16037             if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment
16038 
16039             if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) {
16040               ref.assign(7);
16041               arg1 = compile(ss,s,depth1,ref,is_single); // Lvalue slot
16042               arg2 = compile(s + 1,se,depth1,0,is_single); // Value to assign
16043 
16044               if (*ref==1) { // Vector value (scalar): V[k] = scalar
16045                 _cimg_mp_check_type(arg2,2,1,0);
16046                 arg3 = ref[1]; // Vector slot
16047                 arg4 = ref[2]; // Index
16048                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16049                 CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg2).
16050                   move_to(code);
16051                 _cimg_mp_return(arg2);
16052               }
16053 
16054               if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar
16055                 if (!is_single) is_parallelizable = false;
16056                 _cimg_mp_check_type(arg2,2,1,0);
16057                 p1 = ref[1]; // Index
16058                 is_relative = (bool)ref[2];
16059                 arg3 = ref[3]; // Offset
16060                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16061                 if (p1!=~0U) {
16062                   if (!listout) _cimg_mp_return(arg2);
16063                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
16064                                       arg2,p1,arg3).move_to(code);
16065                 } else {
16066                   if (!imgout) _cimg_mp_return(arg2);
16067                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
16068                                       arg2,arg3).move_to(code);
16069                 }
16070                 _cimg_mp_return(arg2);
16071               }
16072 
16073               if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar
16074                 if (!is_single) is_parallelizable = false;
16075                 _cimg_mp_check_type(arg2,2,1,0);
16076                 p1 = ref[1]; // Index
16077                 is_relative = (bool)ref[2];
16078                 arg3 = ref[3]; // X
16079                 arg4 = ref[4]; // Y
16080                 arg5 = ref[5]; // Z
16081                 arg6 = ref[6]; // C
16082                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16083                 if (p1!=~0U) {
16084                   if (!listout) _cimg_mp_return(arg2);
16085                   CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
16086                                       arg2,p1,arg3,arg4,arg5,arg6).move_to(code);
16087                 } else {
16088                   if (!imgout) _cimg_mp_return(arg2);
16089                   CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
16090                                       arg2,arg3,arg4,arg5,arg6).move_to(code);
16091                 }
16092                 _cimg_mp_return(arg2);
16093               }
16094 
16095               if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value
16096                 if (!is_single) is_parallelizable = false;
16097                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16098                 p1 = ref[1]; // Index
16099                 is_relative = (bool)ref[2];
16100                 arg3 = ref[3]; // Offset
16101                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16102                 if (p1!=~0U) {
16103                   if (!listout) _cimg_mp_return(arg2);
16104                   if (_cimg_mp_is_scalar(arg2))
16105                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
16106                                         arg2,p1,arg3).move_to(code);
16107                   else
16108                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
16109                                         arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code);
16110                 } else {
16111                   if (!imgout) _cimg_mp_return(arg2);
16112                   if (_cimg_mp_is_scalar(arg2))
16113                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
16114                                         arg2,arg3).move_to(code);
16115                   else
16116                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
16117                                         arg2,arg3,_cimg_mp_size(arg2)).move_to(code);
16118                 }
16119                 _cimg_mp_return(arg2);
16120               }
16121 
16122               if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value
16123                 if (!is_single) is_parallelizable = false;
16124                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16125                 p1 = ref[1]; // Index
16126                 is_relative = (bool)ref[2];
16127                 arg3 = ref[3]; // X
16128                 arg4 = ref[4]; // Y
16129                 arg5 = ref[5]; // Z
16130                 if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16131                 if (p1!=~0U) {
16132                   if (!listout) _cimg_mp_return(arg2);
16133                   if (_cimg_mp_is_scalar(arg2))
16134                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
16135                                         arg2,p1,arg3,arg4,arg5).move_to(code);
16136                   else
16137                     CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
16138                                         arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
16139                 } else {
16140                   if (!imgout) _cimg_mp_return(arg2);
16141                   if (_cimg_mp_is_scalar(arg2))
16142                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
16143                                         arg2,arg3,arg4,arg5).move_to(code);
16144                   else
16145                     CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
16146                                         arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
16147                 }
16148                 _cimg_mp_return(arg2);
16149               }
16150 
16151               if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value
16152                 _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16153                 if (_cimg_mp_is_vector(arg2)) // From vector
16154                   CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
16155                     move_to(code);
16156                 else // From scalar
16157                   CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
16158                     move_to(code);
16159                 _cimg_mp_return(arg1);
16160               }
16161 
16162               if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s = scalar
16163                 _cimg_mp_check_type(arg2,2,1,0);
16164                 CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
16165                 _cimg_mp_return(arg1);
16166               }
16167             }
16168 
16169             // No assignment expressions match -> error
16170             *se = saved_char;
16171             cimg::strellipsize(variable_name,64);
16172             s0 = ss - 4>expr._data?ss - 4:expr._data;
16173             cimg::strellipsize(s0,64);
16174             throw CImgArgumentException("[" cimg_appname "_math_parser] "
16175                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
16176                                         "in expression '%s%s%s'.",
16177                                         pixel_type(),_cimg_mp_calling_function,s_op,
16178                                         arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"",
16179                                         variable_name._data,
16180                                         s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
16181           }
16182 
16183         // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++.
16184         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
16185           if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps &&
16186               level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=)
16187             _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='");
16188 
16189             ref.assign(7);
16190             arg1 = compile(ss,ns,depth1,ref,is_single); // Vector slot
16191             arg2 = compile(s + 1,se,depth1,0,is_single); // Right operand
16192             _cimg_mp_check_type(arg1,1,2,2);
16193             _cimg_mp_check_type(arg2,2,3,2);
16194             if (_cimg_mp_is_vector(arg2)) { // Complex **= complex
16195               if (*ps=='*')
16196                 CImg<ulongT>::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code);
16197               else if (*ps=='/')
16198                 CImg<ulongT>::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code);
16199               else
16200                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code);
16201             } else { // Complex **= scalar
16202               if (*ps=='*') {
16203                 if (arg2==1) _cimg_mp_return(arg1);
16204                 self_vector_s(arg1,mp_self_mul,arg2);
16205               } else if (*ps=='/') {
16206                 if (arg2==1) _cimg_mp_return(arg1);
16207                 self_vector_s(arg1,mp_self_div,arg2);
16208               } else {
16209                 if (arg2==1) _cimg_mp_return(arg1);
16210                 CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code);
16211               }
16212             }
16213 
16214             // Write computed value back in image if necessary.
16215             if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value
16216               if (!is_single) is_parallelizable = false;
16217               p1 = ref[1]; // Index
16218               is_relative = (bool)ref[2];
16219               arg3 = ref[3]; // Offset
16220               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16221               if (p1!=~0U) {
16222                 if (!listout) _cimg_mp_return(arg1);
16223                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
16224                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
16225               } else {
16226                 if (!imgout) _cimg_mp_return(arg1);
16227                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
16228                                     arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
16229               }
16230 
16231             } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value
16232               if (!is_single) is_parallelizable = false;
16233               p1 = ref[1]; // Index
16234               is_relative = (bool)ref[2];
16235               arg3 = ref[3]; // X
16236               arg4 = ref[4]; // Y
16237               arg5 = ref[5]; // Z
16238               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16239               if (p1!=~0U) {
16240                 if (!listout) _cimg_mp_return(arg1);
16241                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
16242                                     arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
16243               } else {
16244                 if (!imgout) _cimg_mp_return(arg1);
16245                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
16246                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
16247               }
16248             }
16249 
16250             _cimg_mp_return(arg1);
16251           }
16252 
16253         for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
16254           if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' ||
16255                           *ps=='&' || *ps=='^' || *ps=='|' ||
16256                           (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) &&
16257               level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
16258             switch (*ps) {
16259             case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break;
16260             case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break;
16261             case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break;
16262             case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break;
16263             case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break;
16264             case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break;
16265             case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break;
16266             case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break;
16267             case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break;
16268             default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break;
16269             }
16270             s1 = *ps=='>' || *ps=='<'?ns:ps;
16271 
16272             ref.assign(7);
16273             arg1 = compile(ss,s1,depth1,ref,is_single); // Variable slot
16274             arg2 = compile(s + 1,se,depth1,0,is_single); // Value to apply
16275 
16276             // Check for particular case to be simplified.
16277             if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1);
16278             if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1);
16279 
16280             // Apply operator on a copy to prevent modifying a constant or a variable.
16281             if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) {
16282               if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
16283               else arg1 = scalar1(mp_copy,arg1);
16284             }
16285 
16286             if (*ref==1) { // Vector value (scalar): V[k] += scalar
16287               _cimg_mp_check_type(arg2,2,1,0);
16288               arg3 = ref[1]; // Vector slot
16289               arg4 = ref[2]; // Index
16290               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16291               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
16292               CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1).
16293                 move_to(code);
16294               _cimg_mp_return(arg1);
16295             }
16296 
16297             if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar
16298               if (!is_single) is_parallelizable = false;
16299               _cimg_mp_check_type(arg2,2,1,0);
16300               p1 = ref[1]; // Index
16301               is_relative = (bool)ref[2];
16302               arg3 = ref[3]; // Offset
16303               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16304               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
16305               if (p1!=~0U) {
16306                 if (!listout) _cimg_mp_return(arg1);
16307                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
16308                                     arg1,p1,arg3).move_to(code);
16309               } else {
16310                 if (!imgout) _cimg_mp_return(arg1);
16311                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
16312                                     arg1,arg3).move_to(code);
16313               }
16314               _cimg_mp_return(arg1);
16315             }
16316 
16317             if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar
16318               if (!is_single) is_parallelizable = false;
16319               _cimg_mp_check_type(arg2,2,1,0);
16320               p1 = ref[1]; // Index
16321               is_relative = (bool)ref[2];
16322               arg3 = ref[3]; // X
16323               arg4 = ref[4]; // Y
16324               arg5 = ref[5]; // Z
16325               arg6 = ref[6]; // C
16326               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16327               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
16328               if (p1!=~0U) {
16329                 if (!listout) _cimg_mp_return(arg1);
16330                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
16331                                     arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
16332               } else {
16333                 if (!imgout) _cimg_mp_return(arg1);
16334                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
16335                                     arg1,arg3,arg4,arg5,arg6).move_to(code);
16336               }
16337               _cimg_mp_return(arg1);
16338             }
16339 
16340             if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value
16341               if (!is_single) is_parallelizable = false;
16342               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16343               p1 = ref[1]; // Index
16344               is_relative = (bool)ref[2];
16345               arg3 = ref[3]; // Offset
16346               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16347               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
16348               if (p1!=~0U) {
16349                 if (!listout) _cimg_mp_return(arg1);
16350                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
16351                                     arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
16352               } else {
16353                 if (!imgout) _cimg_mp_return(arg1);
16354                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
16355                                     arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
16356               }
16357               _cimg_mp_return(arg1);
16358             }
16359 
16360             if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value
16361               if (!is_single) is_parallelizable = false;
16362               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16363               p1 = ref[1]; // Index
16364               is_relative = (bool)ref[2];
16365               arg3 = ref[3]; // X
16366               arg4 = ref[4]; // Y
16367               arg5 = ref[5]; // Z
16368               if (p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16369               if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
16370               if (p1!=~0U) {
16371                 if (!listout) _cimg_mp_return(arg1);
16372                 CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
16373                                     arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
16374               } else {
16375                 if (!imgout) _cimg_mp_return(arg1);
16376                 CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
16377                                     arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
16378               }
16379               _cimg_mp_return(arg1);
16380             }
16381 
16382             if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value
16383               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16384               if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector
16385               else self_vector_s(arg1,op,arg2); // Vector += scalar
16386               _cimg_mp_return(arg1);
16387             }
16388 
16389             if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s += scalar
16390               _cimg_mp_check_type(arg2,2,1,0);
16391               CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
16392               _cimg_mp_return(arg1);
16393             }
16394 
16395             variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0;
16396             cimg::strpare(variable_name,false,true);
16397             *se = saved_char;
16398             s0 = ss - 4>expr._data?ss - 4:expr._data;
16399             cimg::strellipsize(s0,64);
16400             throw CImgArgumentException("[" cimg_appname "_math_parser] "
16401                                         "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
16402                                         "in expression '%s%s%s'.",
16403                                         pixel_type(),_cimg_mp_calling_function,s_op,
16404                                         _cimg_mp_is_constant(arg1)?"const ":"",
16405                                         variable_name._data,
16406                                         s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
16407           }
16408 
16409         for (s = ss1; s<se1; ++s)
16410           if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2'
16411             _cimg_mp_op("Operator '?:'");
16412             s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1;
16413             arg1 = compile(ss,s,depth1,0,is_single);
16414             _cimg_mp_check_type(arg1,1,1,0);
16415             if (_cimg_mp_is_constant(arg1)) {
16416               if ((bool)mem[arg1]) return compile(s + 1,*s1!=':'?se:s1,depth1,0,is_single);
16417               else return *s1!=':'?0:compile(++s1,se,depth1,0,is_single);
16418             }
16419             p2 = code._width;
16420             arg2 = compile(s + 1,*s1!=':'?se:s1,depth1,0,is_single);
16421             p3 = code._width;
16422             arg3 = *s1==':'?compile(++s1,se,depth1,0,is_single):
16423               _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
16424             _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
16425             arg4 = _cimg_mp_size(arg2);
16426             if (arg4) pos = vector(arg4); else pos = scalar();
16427             CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
16428                                 p3 - p2,code._width - p3,arg4).move_to(code,p2);
16429             _cimg_mp_return(pos);
16430           }
16431 
16432         for (s = se3, ns = se2; s>ss; --s, --ns)
16433           if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||')
16434             _cimg_mp_op("Operator '||'");
16435             arg1 = compile(ss,s,depth1,0,is_single);
16436             _cimg_mp_check_type(arg1,1,1,0);
16437             if (arg1>0 && arg1<=16) _cimg_mp_return(1);
16438             p2 = code._width;
16439             arg2 = compile(s + 2,se,depth1,0,is_single);
16440             _cimg_mp_check_type(arg2,2,1,0);
16441             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16442               _cimg_mp_constant(mem[arg1] || mem[arg2]);
16443             if (!arg1) _cimg_mp_return(arg2);
16444             pos = scalar();
16445             CImg<ulongT>::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2).
16446               move_to(code,p2);
16447             _cimg_mp_return(pos);
16448           }
16449 
16450         for (s = se3, ns = se2; s>ss; --s, --ns)
16451           if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&')
16452             _cimg_mp_op("Operator '&&'");
16453             arg1 = compile(ss,s,depth1,0,is_single);
16454             _cimg_mp_check_type(arg1,1,1,0);
16455             if (!arg1) _cimg_mp_return(0);
16456             p2 = code._width;
16457             arg2 = compile(s + 2,se,depth1,0,is_single);
16458             _cimg_mp_check_type(arg2,2,1,0);
16459             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16460               _cimg_mp_constant(mem[arg1] && mem[arg2]);
16461             if (arg1>0 && arg1<=16) _cimg_mp_return(arg2);
16462             pos = scalar();
16463             CImg<ulongT>::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2).
16464               move_to(code,p2);
16465             _cimg_mp_return(pos);
16466           }
16467 
16468         for (s = se2; s>ss; --s)
16469           if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|')
16470             _cimg_mp_op("Operator '|'");
16471             arg1 = compile(ss,s,depth1,0,is_single);
16472             arg2 = compile(s + 1,se,depth1,0,is_single);
16473             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16474             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2);
16475             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
16476               if (!arg2) _cimg_mp_return(arg1);
16477               _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2);
16478             }
16479             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
16480               if (!arg1) _cimg_mp_return(arg2);
16481               _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2);
16482             }
16483             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16484               _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]);
16485             if (!arg2) _cimg_mp_return(arg1);
16486             if (!arg1) _cimg_mp_return(arg2);
16487             _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2);
16488           }
16489 
16490         for (s = se2; s>ss; --s)
16491           if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&')
16492             _cimg_mp_op("Operator '&'");
16493             arg1 = compile(ss,s,depth1,0,is_single);
16494             arg2 = compile(s + 1,se,depth1,0,is_single);
16495             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16496             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2);
16497             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2);
16498             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2);
16499             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16500               _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]);
16501             if (!arg1 || !arg2) _cimg_mp_return(0);
16502             _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2);
16503           }
16504 
16505         for (s = se3, ns = se2; s>ss; --s, --ns)
16506           if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=')
16507             _cimg_mp_op("Operator '!='");
16508             arg1 = compile(ss,s,depth1,0,is_single);
16509             arg2 = compile(s + 2,se,depth1,0,is_single);
16510             if (arg1==arg2) _cimg_mp_return(0);
16511             p1 = _cimg_mp_size(arg1);
16512             p2 = _cimg_mp_size(arg2);
16513             if (p1 || p2) {
16514               if (p1 && p2 && p1!=p2) _cimg_mp_return(1);
16515               _cimg_mp_scalar6(mp_vector_neq,arg1,p1,arg2,p2,11,1);
16516             }
16517             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]);
16518             _cimg_mp_scalar2(mp_neq,arg1,arg2);
16519           }
16520 
16521         for (s = se3, ns = se2; s>ss; --s, --ns)
16522           if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==')
16523             _cimg_mp_op("Operator '=='");
16524             arg1 = compile(ss,s,depth1,0,is_single);
16525             arg2 = compile(s + 2,se,depth1,0,is_single);
16526             if (arg1==arg2) _cimg_mp_return(1);
16527             p1 = _cimg_mp_size(arg1);
16528             p2 = _cimg_mp_size(arg2);
16529             if (p1 || p2) {
16530               if (p1 && p2 && p1!=p2) _cimg_mp_return(0);
16531               _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,11,1);
16532             }
16533             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]);
16534             _cimg_mp_scalar2(mp_eq,arg1,arg2);
16535           }
16536 
16537         for (s = se3, ns = se2; s>ss; --s, --ns)
16538           if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=')
16539             _cimg_mp_op("Operator '<='");
16540             arg1 = compile(ss,s,depth1,0,is_single);
16541             arg2 = compile(s + 2,se,depth1,0,is_single);
16542             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16543             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2);
16544             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2);
16545             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2);
16546             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]);
16547             if (arg1==arg2) _cimg_mp_return(1);
16548             _cimg_mp_scalar2(mp_lte,arg1,arg2);
16549           }
16550 
16551         for (s = se3, ns = se2; s>ss; --s, --ns)
16552           if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=')
16553             _cimg_mp_op("Operator '>='");
16554             arg1 = compile(ss,s,depth1,0,is_single);
16555             arg2 = compile(s + 2,se,depth1,0,is_single);
16556             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16557             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2);
16558             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2);
16559             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2);
16560             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]);
16561             if (arg1==arg2) _cimg_mp_return(1);
16562             _cimg_mp_scalar2(mp_gte,arg1,arg2);
16563           }
16564 
16565         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
16566           if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<')
16567             _cimg_mp_op("Operator '<'");
16568             arg1 = compile(ss,s,depth1,0,is_single);
16569             arg2 = compile(s + 1,se,depth1,0,is_single);
16570             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16571             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2);
16572             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2);
16573             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2);
16574             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<mem[arg2]);
16575             if (arg1==arg2) _cimg_mp_return(0);
16576             _cimg_mp_scalar2(mp_lt,arg1,arg2);
16577           }
16578 
16579         for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
16580           if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greather than ('>')
16581             _cimg_mp_op("Operator '>'");
16582             arg1 = compile(ss,s,depth1,0,is_single);
16583             arg2 = compile(s + 1,se,depth1,0,is_single);
16584             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16585             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2);
16586             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2);
16587             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2);
16588             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]);
16589             if (arg1==arg2) _cimg_mp_return(0);
16590             _cimg_mp_scalar2(mp_gt,arg1,arg2);
16591           }
16592 
16593         for (s = se3, ns = se2; s>ss; --s, --ns)
16594           if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<')
16595             _cimg_mp_op("Operator '<<'");
16596             arg1 = compile(ss,s,depth1,0,is_single);
16597             arg2 = compile(s + 2,se,depth1,0,is_single);
16598             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16599             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
16600               _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2);
16601             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
16602               if (!arg2) _cimg_mp_return(arg1);
16603               _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2);
16604             }
16605             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
16606               _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2);
16607             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16608               _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]);
16609             if (!arg1) _cimg_mp_return(0);
16610             if (!arg2) _cimg_mp_return(arg1);
16611             _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2);
16612           }
16613 
16614         for (s = se3, ns = se2; s>ss; --s, --ns)
16615           if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>')
16616             _cimg_mp_op("Operator '>>'");
16617             arg1 = compile(ss,s,depth1,0,is_single);
16618             arg2 = compile(s + 2,se,depth1,0,is_single);
16619             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16620             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
16621               _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2);
16622             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
16623               if (!arg2) _cimg_mp_return(arg1);
16624               _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2);
16625             }
16626             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
16627               _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2);
16628             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16629               _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]);
16630             if (!arg1) _cimg_mp_return(0);
16631             if (!arg2) _cimg_mp_return(arg1);
16632             _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2);
16633           }
16634 
16635         for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
16636           if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
16637               *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
16638               (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
16639                                                                                      *(ps - 1)<='9')))) &&
16640               level[s - expr._data]==clevel) { // Addition ('+')
16641             _cimg_mp_op("Operator '+'");
16642             arg1 = compile(ss,s,depth1,0,is_single);
16643             arg2 = compile(s + 1,se,depth1,0,is_single);
16644             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16645             if (!arg2) _cimg_mp_return(arg1);
16646             if (!arg1) _cimg_mp_return(arg2);
16647             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2);
16648             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2);
16649             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2);
16650             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]);
16651             if (code) { // Try to spot linear case 'a*b + c'.
16652               CImg<ulongT> &pop = code.back();
16653               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
16654                 arg3 = (unsigned int)pop[1];
16655                 arg4 = (unsigned int)pop[2];
16656                 arg5 = (unsigned int)pop[3];
16657                 code.remove();
16658                 CImg<ulongT>::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
16659                 _cimg_mp_return(arg3);
16660               }
16661             }
16662             if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1);
16663             if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2);
16664             _cimg_mp_scalar2(mp_add,arg1,arg2);
16665           }
16666 
16667         for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
16668           if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
16669               *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
16670               (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
16671                                                                                      *(ps - 1)<='9')))) &&
16672               level[s - expr._data]==clevel) { // Subtraction ('-')
16673             _cimg_mp_op("Operator '-'");
16674             arg1 = compile(ss,s,depth1,0,is_single);
16675             arg2 = compile(s + 1,se,depth1,0,is_single);
16676             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16677             if (!arg2) _cimg_mp_return(arg1);
16678             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2);
16679             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2);
16680             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
16681               if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2);
16682               _cimg_mp_vector2_sv(mp_sub,arg1,arg2);
16683             }
16684             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]);
16685             if (!arg1) _cimg_mp_scalar1(mp_minus,arg2);
16686             if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b'.
16687               CImg<ulongT> &pop = code.back();
16688               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
16689                 arg3 = (unsigned int)pop[1];
16690                 arg4 = (unsigned int)pop[2];
16691                 arg5 = (unsigned int)pop[3];
16692                 code.remove();
16693                 CImg<ulongT>::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right),
16694                                      arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code);
16695                 _cimg_mp_return(arg3);
16696               }
16697             }
16698             if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1);
16699             _cimg_mp_scalar2(mp_sub,arg1,arg2);
16700           }
16701 
16702         for (s = se3, ns = se2; s>ss; --s, --ns)
16703           if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**')
16704             _cimg_mp_op("Operator '**'");
16705             arg1 = compile(ss,s,depth1,0,is_single);
16706             arg2 = compile(s + 2,se,depth1,0,is_single);
16707             _cimg_mp_check_type(arg1,1,3,2);
16708             _cimg_mp_check_type(arg2,2,3,2);
16709             if (arg2==1) _cimg_mp_return(arg1);
16710             if (arg1==1) _cimg_mp_return(arg2);
16711             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
16712               pos = vector(2);
16713               CImg<ulongT>::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code);
16714               _cimg_mp_return(pos);
16715             }
16716             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
16717             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
16718             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
16719             if (!arg1 || !arg2) _cimg_mp_return(0);
16720             _cimg_mp_scalar2(mp_mul,arg1,arg2);
16721           }
16722 
16723         for (s = se3, ns = se2; s>ss; --s, --ns)
16724           if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//')
16725             _cimg_mp_op("Operator '//'");
16726             arg1 = compile(ss,s,depth1,0,is_single);
16727             arg2 = compile(s + 2,se,depth1,0,is_single);
16728             _cimg_mp_check_type(arg1,1,3,2);
16729             _cimg_mp_check_type(arg2,2,3,2);
16730             if (arg2==1) _cimg_mp_return(arg1);
16731             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
16732               pos = vector(2);
16733               CImg<ulongT>::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code);
16734               _cimg_mp_return(pos);
16735             }
16736             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
16737             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
16738               pos = vector(2);
16739               CImg<ulongT>::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code);
16740               _cimg_mp_return(pos);
16741             }
16742             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
16743             if (!arg1) _cimg_mp_return(0);
16744             _cimg_mp_scalar2(mp_div,arg1,arg2);
16745           }
16746 
16747         for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*')
16748             _cimg_mp_op("Operator '*'");
16749             arg1 = compile(ss,s,depth1,0,is_single);
16750             arg2 = compile(s + 1,se,depth1,0,is_single);
16751             p2 = _cimg_mp_size(arg2);
16752             if (p2>0 && _cimg_mp_size(arg1)==p2*p2) { // Particular case of matrix multiplication
16753               pos = vector(p2);
16754               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code);
16755               _cimg_mp_return(pos);
16756             }
16757             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16758             if (arg2==1) _cimg_mp_return(arg1);
16759             if (arg1==1) _cimg_mp_return(arg2);
16760             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2);
16761             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
16762             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
16763             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]);
16764 
16765             if (code) { // Try to spot double multiplication 'a*b*c'.
16766               CImg<ulongT> &pop = code.back();
16767               if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
16768                 arg3 = (unsigned int)pop[1];
16769                 arg4 = (unsigned int)pop[2];
16770                 arg5 = (unsigned int)pop[3];
16771                 code.remove();
16772                 CImg<ulongT>::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
16773                 _cimg_mp_return(arg3);
16774               }
16775             }
16776             if (!arg1 || !arg2) _cimg_mp_return(0);
16777             _cimg_mp_scalar2(mp_mul,arg1,arg2);
16778           }
16779 
16780         for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/')
16781             _cimg_mp_op("Operator '/'");
16782             arg1 = compile(ss,s,depth1,0,is_single);
16783             arg2 = compile(s + 1,se,depth1,0,is_single);
16784             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16785             if (arg2==1) _cimg_mp_return(arg1);
16786             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2);
16787             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
16788             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2);
16789             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]);
16790             if (!arg1) _cimg_mp_return(0);
16791             _cimg_mp_scalar2(mp_div,arg1,arg2);
16792           }
16793 
16794         for (s = se2, ns = se1; s>ss; --s, --ns)
16795           if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%')
16796             _cimg_mp_op("Operator '%'");
16797             arg1 = compile(ss,s,depth1,0,is_single);
16798             arg2 = compile(s + 1,se,depth1,0,is_single);
16799             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16800             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2);
16801             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2);
16802             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2);
16803             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16804               _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2]));
16805             _cimg_mp_scalar2(mp_modulo,arg1,arg2);
16806           }
16807 
16808         if (se1>ss) {
16809           if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary plus ('+')
16810             _cimg_mp_op("Operator '+'");
16811             _cimg_mp_return(compile(ss1,se,depth1,0,is_single));
16812           }
16813 
16814           if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus ('-')
16815             _cimg_mp_op("Operator '-'");
16816             arg1 = compile(ss1,se,depth1,0,is_single);
16817             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1);
16818             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]);
16819             _cimg_mp_scalar1(mp_minus,arg1);
16820           }
16821 
16822           if (*ss=='!') { // Logical not ('!')
16823             _cimg_mp_op("Operator '!'");
16824             if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)'
16825               arg1 = compile(ss2,se,depth1,0,is_single);
16826               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
16827               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
16828               _cimg_mp_scalar1(mp_bool,arg1);
16829             }
16830             arg1 = compile(ss1,se,depth1,0,is_single);
16831             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1);
16832             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]);
16833             _cimg_mp_scalar1(mp_logical_not,arg1);
16834           }
16835 
16836           if (*ss=='~') { // Bitwise not ('~')
16837             _cimg_mp_op("Operator '~'");
16838             arg1 = compile(ss1,se,depth1,0,is_single);
16839             if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1);
16840             if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]);
16841             _cimg_mp_scalar1(mp_bitwise_not,arg1);
16842           }
16843         }
16844 
16845         for (s = se3, ns = se2; s>ss; --s, --ns)
16846           if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^')
16847             _cimg_mp_op("Operator '^^'");
16848             arg1 = compile(ss,s,depth1,0,is_single);
16849             arg2 = compile(s + 2,se,depth1,0,is_single);
16850             _cimg_mp_check_type(arg1,1,3,2);
16851             _cimg_mp_check_type(arg2,2,3,2);
16852             if (arg2==1) _cimg_mp_return(arg1);
16853             pos = vector(2);
16854             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
16855               CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code);
16856               _cimg_mp_return(pos);
16857             }
16858             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
16859               CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code);
16860               _cimg_mp_return(pos);
16861             }
16862             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
16863               CImg<ulongT>::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code);
16864               _cimg_mp_return(pos);
16865             }
16866             CImg<ulongT>::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code);
16867             _cimg_mp_return(pos);
16868           }
16869 
16870         for (s = se2; s>ss; --s)
16871           if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^')
16872             _cimg_mp_op("Operator '^'");
16873             arg1 = compile(ss,s,depth1,0,is_single);
16874             arg2 = compile(s + 1,se,depth1,0,is_single);
16875             _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
16876             if (arg2==1) _cimg_mp_return(arg1);
16877             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2);
16878             if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2);
16879             if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2);
16880             if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
16881               _cimg_mp_constant(std::pow(mem[arg1],mem[arg2]));
16882             switch (arg2) {
16883             case 0 : _cimg_mp_return(1);
16884             case 2 : _cimg_mp_scalar1(mp_sqr,arg1);
16885             case 3 : _cimg_mp_scalar1(mp_pow3,arg1);
16886             case 4 : _cimg_mp_scalar1(mp_pow4,arg1);
16887             default :
16888               if (_cimg_mp_is_constant(arg2)) {
16889                 if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); }
16890                 else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); }
16891               }
16892               _cimg_mp_scalar2(mp_pow,arg1,arg2);
16893             }
16894           }
16895 
16896         // Percentage computation.
16897         if (*se1=='%') {
16898           arg1 = compile(ss,se1,depth1,0,is_single);
16899           arg2 = _cimg_mp_is_constant(arg1)?0:constant(100);
16900           if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
16901           if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100);
16902           _cimg_mp_scalar2(mp_div,arg1,arg2);
16903         }
16904 
16905         is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-?
16906         if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment
16907           if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) {
16908             _cimg_mp_op("Operator '++'");
16909             op = mp_self_increment;
16910           } else {
16911             _cimg_mp_op("Operator '--'");
16912             op = mp_self_decrement;
16913           }
16914           ref.assign(7);
16915           arg1 = is_sth?compile(ss2,se,depth1,ref,is_single):
16916             compile(ss,se2,depth1,ref,is_single); // Variable slot
16917 
16918           // Apply operator on a copy to prevent modifying a constant or a variable.
16919           if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_variable(arg1))) {
16920             if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
16921             else arg1 = scalar1(mp_copy,arg1);
16922           }
16923 
16924           if (is_sth) pos = arg1; // Determine return indice, depending on pre/post action
16925           else {
16926             if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1);
16927             else pos = scalar1(mp_copy,arg1);
16928           }
16929 
16930           if (*ref==1) { // Vector value (scalar): V[k]++
16931             arg3 = ref[1]; // Vector slot
16932             arg4 = ref[2]; // Index
16933             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16934             CImg<ulongT>::vector((ulongT)op,arg1,1).move_to(code);
16935             CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4,arg1).
16936               move_to(code);
16937             _cimg_mp_return(pos);
16938           }
16939 
16940           if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++
16941             if (!is_single) is_parallelizable = false;
16942             p1 = ref[1]; // Index
16943             is_relative = (bool)ref[2];
16944             arg3 = ref[3]; // Offset
16945             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16946             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
16947             if (p1!=~0U) {
16948               if (!listout) _cimg_mp_return(pos);
16949               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
16950                                   arg1,p1,arg3).move_to(code);
16951             } else {
16952               if (!imgout) _cimg_mp_return(pos);
16953               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
16954                                   arg1,arg3).move_to(code);
16955             }
16956             _cimg_mp_return(pos);
16957           }
16958 
16959           if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++
16960             if (!is_single) is_parallelizable = false;
16961             p1 = ref[1]; // Index
16962             is_relative = (bool)ref[2];
16963             arg3 = ref[3]; // X
16964             arg4 = ref[4]; // Y
16965             arg5 = ref[5]; // Z
16966             arg6 = ref[6]; // C
16967             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16968             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
16969             if (p1!=~0U) {
16970               if (!listout) _cimg_mp_return(pos);
16971               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
16972                                   arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
16973             } else {
16974               if (!imgout) _cimg_mp_return(pos);
16975               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
16976                                   arg1,arg3,arg4,arg5,arg6).move_to(code);
16977             }
16978             _cimg_mp_return(pos);
16979           }
16980 
16981           if (*ref==4) { // Image value (vector): I/J[_#ind,off]++
16982             if (!is_single) is_parallelizable = false;
16983             p1 = ref[1]; // Index
16984             is_relative = (bool)ref[2];
16985             arg3 = ref[3]; // Offset
16986             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
16987             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
16988             if (p1!=~0U) {
16989               if (!listout) _cimg_mp_return(pos);
16990               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
16991                                   arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
16992             } else {
16993               if (!imgout) _cimg_mp_return(pos);
16994               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
16995                                   arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
16996             }
16997             _cimg_mp_return(pos);
16998           }
16999 
17000           if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++
17001             if (!is_single) is_parallelizable = false;
17002             p1 = ref[1]; // Index
17003             is_relative = (bool)ref[2];
17004             arg3 = ref[3]; // X
17005             arg4 = ref[4]; // Y
17006             arg5 = ref[5]; // Z
17007             if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
17008             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
17009             if (p1!=~0U) {
17010               if (!listout) _cimg_mp_return(pos);
17011               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
17012                                   arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17013             } else {
17014               if (!imgout) _cimg_mp_return(pos);
17015               CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
17016                                   arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
17017             }
17018             _cimg_mp_return(pos);
17019           }
17020 
17021           if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++
17022             self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
17023             _cimg_mp_return(pos);
17024           }
17025 
17026           if (_cimg_mp_is_variable(arg1)) { // Scalar variable: s++
17027             CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
17028             _cimg_mp_return(pos);
17029           }
17030 
17031           if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1));
17032           else variable_name.assign(ss,(unsigned int)(se1 - ss));
17033           variable_name.back() = 0;
17034           cimg::strpare(variable_name,false,true);
17035           *se = saved_char;
17036           cimg::strellipsize(variable_name,64);
17037           s0 = ss - 4>expr._data?ss - 4:expr._data;
17038           cimg::strellipsize(s0,64);
17039           throw CImgArgumentException("[" cimg_appname "_math_parser] "
17040                                       "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
17041                                       "in expression '%s%s%s'.",
17042                                       pixel_type(),_cimg_mp_calling_function,s_op,
17043                                       _cimg_mp_is_constant(arg1)?"const ":"",
17044                                       variable_name._data,
17045                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17046         }
17047 
17048         // Array-like access to vectors and  image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'.
17049         if (*se1==']' && *ss!='[') {
17050           _cimg_mp_op("Value accessor '[]'");
17051           is_relative = *ss=='j' || *ss=='J';
17052           s0 = s1 = std::strchr(ss,'['); if (s0) { do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); }
17053 
17054           if ((*ss=='I' || *ss=='J') && *ss1=='[' &&
17055               (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a vector
17056             if (*ss2=='#') { // Index specified
17057               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17058               p1 = compile(ss3,s0++,depth1,0,is_single);
17059               _cimg_mp_check_list(false);
17060             } else { p1 = ~0U; s0 = ss2; }
17061             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17062             p2 = 1 + (p1!=~0U);
17063             arg1 = compile(s0,s1,depth1,0,is_single); // Offset
17064             _cimg_mp_check_type(arg1,p2,1,0);
17065             arg2 = ~0U;
17066             if (s1<se1) {
17067               arg2 = compile(++s1,se1,depth1,0,is_single); // Boundary
17068               _cimg_mp_check_type(arg2,p2 + 1,1,0);
17069             }
17070             if (p_ref && arg2==~0U) {
17071               *p_ref = 4;
17072               p_ref[1] = p1;
17073               p_ref[2] = (unsigned int)is_relative;
17074               p_ref[3] = arg1;
17075               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
17076               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17077             }
17078             p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
17079             if (p1==~0U) p2 = imgin._spectrum;
17080             else if (_cimg_mp_is_constant(p1)) {
17081               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17082               p2 = listin[p3]._spectrum;
17083             }
17084             _cimg_mp_check_vector0(p2);
17085             pos = vector(p2);
17086             if (p1!=~0U) {
17087               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff),
17088                                   pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
17089             } else {
17090               need_input_copy = true;
17091               CImg<ulongT>::vector((ulongT)(is_relative?mp_Joff:mp_Ioff),
17092                                   pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
17093             }
17094             _cimg_mp_return(pos);
17095           }
17096 
17097           if ((*ss=='i' || *ss=='j') && *ss1=='[' &&
17098               (reserved_label[*ss]==~0U || !_cimg_mp_is_vector(reserved_label[*ss]))) { // Image value as a scalar
17099             if (*ss2=='#') { // Index specified
17100               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17101               p1 = compile(ss3,s0++,depth1,0,is_single);
17102             } else { p1 = ~0U; s0 = ss2; }
17103             s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17104             arg1 = compile(s0,s1,depth1,0,is_single); // Offset
17105             arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):~0U; // Boundary
17106             if (p_ref && arg2==~0U) {
17107               *p_ref = 2;
17108               p_ref[1] = p1;
17109               p_ref[2] = (unsigned int)is_relative;
17110               p_ref[3] = arg1;
17111               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
17112               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17113             }
17114             if (p1!=~0U) {
17115               if (!listin) _cimg_mp_return(0);
17116               pos = scalar3(is_relative?mp_list_joff:mp_list_ioff,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
17117             } else {
17118               if (!imgin) _cimg_mp_return(0);
17119               need_input_copy = true;
17120               pos = scalar2(is_relative?mp_joff:mp_ioff,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
17121             }
17122             memtype[pos] = -2; // Prevent from being used in further optimization
17123             _cimg_mp_return(pos);
17124           }
17125 
17126           s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
17127           if (s0>ss) { // Vector value
17128             arg1 = compile(ss,s0,depth1,0,is_single);
17129             if (_cimg_mp_is_scalar(arg1)) {
17130               variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
17131               *se = saved_char;
17132               cimg::strellipsize(variable_name,64);
17133               s0 = ss - 4>expr._data?ss - 4:expr._data;
17134               cimg::strellipsize(s0,64);
17135               throw CImgArgumentException("[" cimg_appname "_math_parser] "
17136                                           "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', "
17137                                           "in expression '%s%s%s'.",
17138                                           pixel_type(),_cimg_mp_calling_function,s_op,
17139                                           variable_name._data,
17140                                           s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17141 
17142             }
17143             s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17144 
17145             if (s1<se1) { // Two arguments -> sub-vector extraction
17146               p1 = _cimg_mp_size(arg1);
17147               arg2 = compile(++s0,s1,depth1,0,is_single); // Starting indice
17148               arg3 = compile(++s1,se1,depth1,0,is_single); // Length
17149               _cimg_mp_check_constant(arg3,2,3);
17150               arg3 = (unsigned int)mem[arg3];
17151               pos = vector(arg3);
17152               CImg<ulongT>::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3).move_to(code);
17153               _cimg_mp_return(pos);
17154             }
17155 
17156             // One argument -> vector value reference
17157             arg2 = compile(++s0,se1,depth1,0,is_single);
17158             if (_cimg_mp_is_constant(arg2)) { // Constant index
17159               nb = (int)mem[arg2];
17160               if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb);
17161               variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0;
17162               *se = saved_char;
17163               cimg::strellipsize(variable_name,64);
17164               s0 = ss - 4>expr._data?ss - 4:expr._data;
17165               cimg::strellipsize(s0,64);
17166               throw CImgArgumentException("[" cimg_appname "_math_parser] "
17167                                           "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' "
17168                                           "(vector '%s' has dimension %u), "
17169                                           "in expression '%s%s%s'.",
17170                                           pixel_type(),_cimg_mp_calling_function,
17171                                           variable_name._data,nb,
17172                                           variable_name._data,_cimg_mp_size(arg1),
17173                                           s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17174             }
17175             if (p_ref) {
17176               *p_ref = 1;
17177               p_ref[1] = arg1;
17178               p_ref[2] = arg2;
17179               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2; // Prevent from being used in further optimization
17180             }
17181             pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2);
17182             memtype[pos] = -2; // Prevent from being used in further optimization
17183             _cimg_mp_return(pos);
17184           }
17185         }
17186 
17187         // Look for a function call, an access to image value, or a parenthesis.
17188         if (*se1==')') {
17189           if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_single)); // Simple parentheses
17190           _cimg_mp_op("Value accessor '()'");
17191           is_relative = *ss=='j' || *ss=='J';
17192           s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while ((signed char)*s1<=' '); cimg::swap(*s0,*++s1); }
17193 
17194           // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions)
17195           if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar
17196             if (*ss2=='#') { // Index specified
17197               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17198               p1 = compile(ss3,s0++,depth1,0,is_single);
17199               _cimg_mp_check_list(false);
17200             } else { p1 = ~0U; s0 = ss2; }
17201             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
17202             arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
17203             arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
17204             arg4 = arg5 = ~0U;
17205             if (s0<se1) {
17206               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17207               arg1 = compile(s0,s1,depth1,0,is_single);
17208               if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
17209                 p2 = _cimg_mp_size(arg1);
17210                 ++arg1;
17211                 if (p2>1) {
17212                   arg2 = arg1 + 1;
17213                   if (p2>2) arg3 = arg2 + 1;
17214                 }
17215                 if (s1<se1) {
17216                   s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17217                   arg4 = compile(s1,s2,depth1,0,is_single);
17218                   arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
17219                 }
17220               } else if (s1<se1) {
17221                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17222                 arg2 = compile(s1,s2,depth1,0,is_single);
17223                 if (s2<se1) {
17224                   s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17225                   arg3 = compile(s2,s3,depth1,0,is_single);
17226                   if (s3<se1) {
17227                     s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17228                     arg4 = compile(s3,s2,depth1,0,is_single);
17229                     arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
17230                   }
17231                 }
17232               }
17233             }
17234             if (p_ref && arg4==~0U && arg5==~0U) {
17235               *p_ref = 5;
17236               p_ref[1] = p1;
17237               p_ref[2] = (unsigned int)is_relative;
17238               p_ref[3] = arg1;
17239               p_ref[4] = arg2;
17240               p_ref[5] = arg3;
17241               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
17242               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17243               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
17244               if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
17245             }
17246             p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
17247             if (p1==~0U) p2 = imgin._spectrum;
17248             else if (_cimg_mp_is_constant(p1)) {
17249               p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17250               p2 = listin[p3]._spectrum;
17251             }
17252             _cimg_mp_check_vector0(p2);
17253             pos = vector(p2);
17254             if (p1!=~0U)
17255               CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz),
17256                                    pos,p1,arg1,arg2,arg3,
17257                                    arg4==~0U?_cimg_mp_interpolation:arg4,
17258                                    arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
17259             else {
17260               need_input_copy = true;
17261               CImg<ulongT>::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz),
17262                                   pos,arg1,arg2,arg3,
17263                                   arg4==~0U?_cimg_mp_interpolation:arg4,
17264                                   arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
17265             }
17266             _cimg_mp_return(pos);
17267           }
17268 
17269           // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)
17270           if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar
17271             if (*ss2=='#') { // Index specified
17272               s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17273               p1 = compile(ss3,s0++,depth1,0,is_single);
17274             } else { p1 = ~0U; s0 = ss2; }
17275             arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
17276             arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
17277             arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
17278             arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
17279             arg5 = arg6 = ~0U;
17280             if (s0<se1) {
17281               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17282               arg1 = compile(s0,s1,depth1,0,is_single);
17283               if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
17284                 p2 = _cimg_mp_size(arg1);
17285                 ++arg1;
17286                 if (p2>1) {
17287                   arg2 = arg1 + 1;
17288                   if (p2>2) {
17289                     arg3 = arg2 + 1;
17290                     if (p2>3) arg4 = arg3 + 1;
17291                   }
17292                 }
17293                 if (s1<se1) {
17294                   s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17295                   arg5 = compile(s1,s2,depth1,0,is_single);
17296                   arg6 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
17297                 }
17298               } else if (s1<se1) {
17299                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17300                 arg2 = compile(s1,s2,depth1,0,is_single);
17301                 if (s2<se1) {
17302                   s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17303                   arg3 = compile(s2,s3,depth1,0,is_single);
17304                   if (s3<se1) {
17305                     s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17306                     arg4 = compile(s3,s2,depth1,0,is_single);
17307                     if (s2<se1) {
17308                       s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17309                       arg5 = compile(s2,s3,depth1,0,is_single);
17310                       arg6 = s3<se1?compile(++s3,se1,depth1,0,is_single):~0U;
17311                     }
17312                   }
17313                 }
17314               }
17315             }
17316             if (p_ref && arg5==~0U && arg6==~0U) {
17317               *p_ref = 3;
17318               p_ref[1] = p1;
17319               p_ref[2] = (unsigned int)is_relative;
17320               p_ref[3] = arg1;
17321               p_ref[4] = arg2;
17322               p_ref[5] = arg3;
17323               p_ref[6] = arg4;
17324               if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -2; // Prevent from being used in further optimization
17325               if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -2;
17326               if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -2;
17327               if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -2;
17328               if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -2;
17329             }
17330 
17331             if (p1!=~0U) {
17332               if (!listin) _cimg_mp_return(0);
17333               pos = scalar7(is_relative?mp_list_jxyzc:mp_list_ixyzc,
17334                             p1,arg1,arg2,arg3,arg4,
17335                             arg5==~0U?_cimg_mp_interpolation:arg5,
17336                             arg6==~0U?_cimg_mp_boundary:arg6);
17337             } else {
17338               if (!imgin) _cimg_mp_return(0);
17339               need_input_copy = true;
17340               pos = scalar6(is_relative?mp_jxyzc:mp_ixyzc,
17341                             arg1,arg2,arg3,arg4,
17342                             arg5==~0U?_cimg_mp_interpolation:arg5,
17343                             arg6==~0U?_cimg_mp_boundary:arg6);
17344             }
17345             memtype[pos] = -2; // Prevent from being used in further optimization
17346             _cimg_mp_return(pos);
17347           }
17348 
17349           // Mathematical functions.
17350           switch (*ss) {
17351 
17352           case '_' :
17353             if (*ss1=='(') // Skip arguments
17354               _cimg_mp_return_nan();
17355             break;
17356 
17357           case 'a' :
17358             if (!std::strncmp(ss,"abs(",4)) { // Absolute value
17359               _cimg_mp_op("Function 'abs()'");
17360               arg1 = compile(ss4,se1,depth1,0,is_single);
17361               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_abs,arg1);
17362               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::abs(mem[arg1]));
17363               _cimg_mp_scalar1(mp_abs,arg1);
17364             }
17365 
17366             if (!std::strncmp(ss,"acos(",5)) { // Arccos
17367               _cimg_mp_op("Function 'acos()'");
17368               arg1 = compile(ss5,se1,depth1,0,is_single);
17369               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acos,arg1);
17370               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::acos(mem[arg1]));
17371               _cimg_mp_scalar1(mp_acos,arg1);
17372             }
17373 
17374             if (!std::strncmp(ss,"acosh(",6)) { // Hyperbolic arccosine
17375               _cimg_mp_op("Function 'acosh()'");
17376               arg1 = compile(ss6,se1,depth1,0,is_single);
17377               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acosh,arg1);
17378               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::acosh(mem[arg1]));
17379               _cimg_mp_scalar1(mp_acosh,arg1);
17380             }
17381 
17382             if (!std::strncmp(ss,"asinh(",6)) { // Hyperbolic arcsine
17383               _cimg_mp_op("Function 'asinh()'");
17384               arg1 = compile(ss6,se1,depth1,0,is_single);
17385               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asinh,arg1);
17386               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::asinh(mem[arg1]));
17387               _cimg_mp_scalar1(mp_asinh,arg1);
17388             }
17389 
17390             if (!std::strncmp(ss,"atanh(",6)) { // Hyperbolic arctangent
17391               _cimg_mp_op("Function 'atanh()'");
17392               arg1 = compile(ss6,se1,depth1,0,is_single);
17393               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atanh,arg1);
17394               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::atanh(mem[arg1]));
17395               _cimg_mp_scalar1(mp_atanh,arg1);
17396             }
17397 
17398             if (!std::strncmp(ss,"arg(",4)) { // Nth argument
17399               _cimg_mp_op("Function 'arg()'");
17400               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17401               arg1 = compile(ss4,s1,depth1,0,is_single);
17402               _cimg_mp_check_type(arg1,1,1,0);
17403               s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17404               arg2 = compile(s1,s2,depth1,0,is_single);
17405               p2 = _cimg_mp_size(arg2);
17406               p3 = 3;
17407               CImg<ulongT>::vector((ulongT)mp_arg,0,0,p2,arg1,arg2).move_to(l_opcode);
17408               for (s = ++s2; s<se; ++s) {
17409                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
17410                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
17411                 arg3 = compile(s,ns,depth1,0,is_single);
17412                 _cimg_mp_check_type(arg3,p3,p2?2:1,p2);
17413                 CImg<ulongT>::vector(arg3).move_to(l_opcode);
17414                 ++p3;
17415                 s = ns;
17416               }
17417               (l_opcode>'y').move_to(opcode);
17418               opcode[2] = opcode._height;
17419               if (_cimg_mp_is_constant(arg1)) {
17420                 p3-=1; // Number of args
17421                 arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]);
17422                 if (arg1<p3) _cimg_mp_return(opcode[4 + arg1]);
17423                 if (p2) {
17424                   pos = vector(p2);
17425                   std::memset(&mem[pos] + 1,0,p2*sizeof(double));
17426                   _cimg_mp_return(pos);
17427                 } else _cimg_mp_return(0);
17428               }
17429               pos = opcode[1] = p2?vector(p2):scalar();
17430               opcode.move_to(code);
17431               _cimg_mp_return(pos);
17432             }
17433 
17434             if (!std::strncmp(ss,"asin(",5)) { // Arcsin
17435               _cimg_mp_op("Function 'asin()'");
17436               arg1 = compile(ss5,se1,depth1,0,is_single);
17437               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1);
17438               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::asin(mem[arg1]));
17439               _cimg_mp_scalar1(mp_asin,arg1);
17440             }
17441 
17442             if (!std::strncmp(ss,"atan(",5)) { // Arctan
17443               _cimg_mp_op("Function 'atan()'");
17444               arg1 = compile(ss5,se1,depth1,0,is_single);
17445               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1);
17446               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::atan(mem[arg1]));
17447               _cimg_mp_scalar1(mp_atan,arg1);
17448             }
17449 
17450             if (!std::strncmp(ss,"atan2(",6)) { // Arctan2
17451               _cimg_mp_op("Function 'atan2()'");
17452               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17453               arg1 = compile(ss6,s1,depth1,0,is_single);
17454               arg2 = compile(++s1,se1,depth1,0,is_single);
17455               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
17456               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_atan2,arg1,arg2);
17457               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_atan2,arg1,arg2);
17458               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_atan2,arg1,arg2);
17459               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
17460                 _cimg_mp_constant(std::atan2(mem[arg1],mem[arg2]));
17461               _cimg_mp_scalar2(mp_atan2,arg1,arg2);
17462             }
17463             break;
17464 
17465           case 'b' :
17466             if (!std::strncmp(ss,"bool(",5)) { // Boolean cast
17467               _cimg_mp_op("Function 'bool()'");
17468               arg1 = compile(ss5,se1,depth1,0,is_single);
17469               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
17470               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]);
17471               _cimg_mp_scalar1(mp_bool,arg1);
17472             }
17473 
17474             if (!std::strncmp(ss,"break(",6)) { // Complex absolute value
17475               if (pexpr[se2 - expr._data]=='(') { // no arguments?
17476                 CImg<ulongT>::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code);
17477                 _cimg_mp_return_nan();
17478               }
17479             }
17480 
17481             if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test)
17482               _cimg_mp_op("Function 'breakpoint()'");
17483               if (pexpr[se2 - expr._data]=='(') { // no arguments?
17484                 CImg<ulongT>::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code);
17485                 _cimg_mp_return_nan();
17486               }
17487             }
17488             break;
17489 
17490           case 'c' :
17491             if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value
17492               _cimg_mp_op("Function 'cabs()'");
17493               arg1 = compile(ss5,se1,depth1,0,is_single);
17494               _cimg_mp_check_type(arg1,0,2,2);
17495               _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2);
17496             }
17497 
17498             if (!std::strncmp(ss,"carg(",5)) { // Complex argument
17499               _cimg_mp_op("Function 'carg()'");
17500               arg1 = compile(ss5,se1,depth1,0,is_single);
17501               _cimg_mp_check_type(arg1,0,2,2);
17502               _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1);
17503             }
17504 
17505             if (!std::strncmp(ss,"cats(",5)) { // Concatenate strings
17506               _cimg_mp_op("Function 'cats()'");
17507               CImg<ulongT>::vector((ulongT)mp_cats,0,0,0).move_to(l_opcode);
17508               arg1 = 0;
17509               for (s = ss5; s<se; ++s) {
17510                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
17511                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
17512                 arg1 = compile(s,ns,depth1,0,is_single);
17513                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
17514                 s = ns;
17515               }
17516               _cimg_mp_check_constant(arg1,1,3); // Last argument = output vector size
17517               l_opcode.remove();
17518               (l_opcode>'y').move_to(opcode);
17519               p1 = (unsigned int)mem[arg1];
17520               pos = vector(p1);
17521               opcode[1] = pos;
17522               opcode[2] = p1;
17523               opcode[3] = opcode._height;
17524               opcode.move_to(code);
17525               _cimg_mp_return(pos);
17526             }
17527 
17528             if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root
17529               _cimg_mp_op("Function 'cbrt()'");
17530               arg1 = compile(ss5,se1,depth1,0,is_single);
17531               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1);
17532               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1]));
17533               _cimg_mp_scalar1(mp_cbrt,arg1);
17534             }
17535 
17536             if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate
17537               _cimg_mp_op("Function 'cconj()'");
17538               arg1 = compile(ss6,se1,depth1,0,is_single);
17539               _cimg_mp_check_type(arg1,0,2,2);
17540               pos = vector(2);
17541               CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1).move_to(code);
17542               _cimg_mp_return(pos);
17543             }
17544 
17545             if (!std::strncmp(ss,"ceil(",5)) { // Ceil
17546               _cimg_mp_op("Function 'ceil()'");
17547               arg1 = compile(ss5,se1,depth1,0,is_single);
17548               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1);
17549               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1]));
17550               _cimg_mp_scalar1(mp_ceil,arg1);
17551             }
17552 
17553             if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential
17554               _cimg_mp_op("Function 'cexp()'");
17555               arg1 = compile(ss5,se1,depth1,0,is_single);
17556               _cimg_mp_check_type(arg1,0,2,2);
17557               pos = vector(2);
17558               CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1).move_to(code);
17559               _cimg_mp_return(pos);
17560             }
17561 
17562             if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm
17563               _cimg_mp_op("Function 'clog()'");
17564               arg1 = compile(ss5,se1,depth1,0,is_single);
17565               _cimg_mp_check_type(arg1,0,2,2);
17566               pos = vector(2);
17567               CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1).move_to(code);
17568               _cimg_mp_return(pos);
17569             }
17570 
17571             if (!std::strncmp(ss,"continue(",9)) { // Complex absolute value
17572               if (pexpr[se2 - expr._data]=='(') { // no arguments?
17573                 CImg<ulongT>::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code);
17574                 _cimg_mp_return_nan();
17575               }
17576             }
17577 
17578             if (!std::strncmp(ss,"copy(",5)) { // Memory copy
17579               _cimg_mp_op("Function 'copy()'");
17580               ref.assign(14);
17581               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17582               arg1 = p1 = compile(ss5,s1,depth1,ref,is_single);
17583               s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17584               arg2 = compile(s1,s2,depth1,ref._data + 7,is_single);
17585               arg3 = ~0U; arg4 = arg5 = arg6 = 1;
17586               if (s2<se1) {
17587                 s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17588                 arg3 = compile(s2,s3,depth1,0,is_single);
17589                 if (s3<se1) {
17590                   s1 = ++s3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17591                   arg4 = compile(s3,s1,depth1,0,is_single);
17592                   if (s1<se1) {
17593                     s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17594                     arg5 = compile(s1,s2,depth1,0,is_single);
17595                     arg6 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
17596                   }
17597                 }
17598               }
17599               if (_cimg_mp_is_vector(arg1) && !ref[0]) ++arg1;
17600               if (_cimg_mp_is_vector(arg2)) {
17601                 if (arg3==~0U) arg3 = _cimg_mp_size(arg2);
17602                 if (!ref[7]) ++arg2;
17603               }
17604               if (arg3==~0U) arg3 = 1;
17605               _cimg_mp_check_type(arg3,3,1,0);
17606               _cimg_mp_check_type(arg4,4,1,0);
17607               _cimg_mp_check_type(arg5,5,1,0);
17608               _cimg_mp_check_type(arg6,5,1,0);
17609               CImg<ulongT>(1,22).move_to(code);
17610               code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6);
17611               code.back().get_shared_rows(8,21).fill(ref);
17612               _cimg_mp_return(p1);
17613             }
17614 
17615             if (!std::strncmp(ss,"cos(",4)) { // Cosine
17616               _cimg_mp_op("Function 'cos()'");
17617               arg1 = compile(ss4,se1,depth1,0,is_single);
17618               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1);
17619               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1]));
17620               _cimg_mp_scalar1(mp_cos,arg1);
17621             }
17622 
17623             if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine
17624               _cimg_mp_op("Function 'cosh()'");
17625               arg1 = compile(ss5,se1,depth1,0,is_single);
17626               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1);
17627               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1]));
17628               _cimg_mp_scalar1(mp_cosh,arg1);
17629             }
17630 
17631             if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time)
17632               _cimg_mp_op("Function 'critical()'");
17633               p1 = code._width;
17634               arg1 = compile(ss + 9,se1,depth1,p_ref,true);
17635               CImg<ulongT>::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1);
17636               _cimg_mp_return(arg1);
17637             }
17638 
17639             if (!std::strncmp(ss,"crop(",5)) { // Image crop
17640               _cimg_mp_op("Function 'crop()'");
17641               if (*ss5=='#') { // Index specified
17642                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17643                 p1 = compile(ss6,s0++,depth1,0,is_single);
17644                 _cimg_mp_check_list(false);
17645               } else { p1 = ~0U; s0 = ss5; need_input_copy = true; }
17646               pos = 0;
17647               is_sth = false; // Coordinates specified as a vector?
17648               if (ss5<se1) for (s = s0; s<se; ++s, ++pos) {
17649                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
17650                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
17651                 arg1 = compile(s,ns,depth1,0,is_single);
17652                 if (!pos && _cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
17653                   opcode = CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
17654                                                   arg1 + (ulongT)_cimg_mp_size(arg1));
17655                   opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode);
17656                   is_sth = true;
17657                 } else {
17658                   _cimg_mp_check_type(arg1,pos + 1,1,0);
17659                   CImg<ulongT>::vector(arg1).move_to(l_opcode);
17660                 }
17661                 s = ns;
17662               }
17663               (l_opcode>'y').move_to(opcode);
17664 
17665               arg1 = 0; arg2 = (p1!=~0U);
17666               switch (opcode._height) {
17667               case 0 : case 1 :
17668                 CImg<ulongT>::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode);
17669                 break;
17670               case 2 :
17671                 CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode);
17672                 arg1 = arg2?3:2;
17673                 break;
17674               case 3 :
17675                 CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode);
17676                 arg1 = arg2?3:2;
17677                 break;
17678               case 4 :
17679                 CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary).
17680                   move_to(opcode);
17681                 arg1 = (is_sth?2:1) + arg2;
17682                 break;
17683               case 5 :
17684                 CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]).
17685                   move_to(opcode);
17686                 arg1 = (is_sth?2:1) + arg2;
17687                 break;
17688               case 6 :
17689                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
17690                                     _cimg_mp_boundary).move_to(opcode);
17691                 arg1 = (is_sth?2:4) + arg2;
17692                 break;
17693               case 7 :
17694                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
17695                                     opcode[6]).move_to(opcode);
17696                 arg1 = (is_sth?2:4) + arg2;
17697                 break;
17698               case 8 :
17699                 CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6],
17700                                     opcode[7],_cimg_mp_boundary).move_to(opcode);
17701                 arg1 = (is_sth?2:5) + arg2;
17702                 break;
17703               case 9 :
17704                 arg1 = (is_sth?2:5) + arg2;
17705                 break;
17706               default : // Error -> too much arguments
17707                 *se = saved_char;
17708                 s0 = ss - 4>expr._data?ss - 4:expr._data;
17709                 cimg::strellipsize(s0,64);
17710                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
17711                                             "CImg<%s>::%s: %s: Too much arguments specified, "
17712                                             "in expression '%s%s%s'.",
17713                                             pixel_type(),_cimg_mp_calling_function,s_op,
17714                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17715               }
17716 
17717               _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0);
17718               _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0);
17719               _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0);
17720               _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0);
17721               if (opcode[4]!=(ulongT)~0U) {
17722                 _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3);
17723                 opcode[4] = (ulongT)mem[opcode[4]];
17724               }
17725               if (opcode[5]!=(ulongT)~0U) {
17726                 _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3);
17727                 opcode[5] = (ulongT)mem[opcode[5]];
17728               }
17729               if (opcode[6]!=(ulongT)~0U) {
17730                 _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3);
17731                 opcode[6] = (ulongT)mem[opcode[6]];
17732               }
17733               if (opcode[7]!=(ulongT)~0U) {
17734                 _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3);
17735                 opcode[7] = (ulongT)mem[opcode[7]];
17736               }
17737               _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0);
17738 
17739               if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U ||
17740                   opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) {
17741                 if (p1!=~0U) {
17742                   _cimg_mp_check_constant(p1,1,1);
17743                   p1 = (unsigned int)cimg::mod((int)mem[p1],listin.width());
17744                 }
17745                 const CImg<T> &img = p1!=~0U?listin[p1]:imgin;
17746                 if (!img) {
17747                   *se = saved_char;
17748                   s0 = ss - 4>expr._data?ss - 4:expr._data;
17749                   cimg::strellipsize(s0,64);
17750                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
17751                                               "CImg<%s>::%s: %s: Cannot crop empty image when "
17752                                               "some xyzc-coordinates are unspecified, in expression '%s%s%s'.",
17753                                               pixel_type(),_cimg_mp_calling_function,s_op,
17754                                               s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
17755                 }
17756                 if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width;
17757                 if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height;
17758                 if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth;
17759                 if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum;
17760               }
17761 
17762               pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7]));
17763               CImg<ulongT>::vector((ulongT)mp_crop,
17764                                   pos,p1,
17765                                   *opcode,opcode[1],opcode[2],opcode[3],
17766                                   opcode[4],opcode[5],opcode[6],opcode[7],
17767                                   opcode[8]).move_to(code);
17768               _cimg_mp_return(pos);
17769             }
17770 
17771             if (!std::strncmp(ss,"cross(",6)) { // Cross product
17772               _cimg_mp_op("Function 'cross()'");
17773               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17774               arg1 = compile(ss6,s1,depth1,0,is_single);
17775               arg2 = compile(++s1,se1,depth1,0,is_single);
17776               _cimg_mp_check_type(arg1,1,2,3);
17777               _cimg_mp_check_type(arg2,2,2,3);
17778               pos = vector(3);
17779               CImg<ulongT>::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code);
17780               _cimg_mp_return(pos);
17781             }
17782 
17783             if (!std::strncmp(ss,"cut(",4)) { // Cut
17784               _cimg_mp_op("Function 'cut()'");
17785               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17786               arg1 = compile(ss4,s1,depth1,0,is_single);
17787               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17788               arg2 = compile(++s1,s2,depth1,0,is_single);
17789               arg3 = compile(++s2,se1,depth1,0,is_single);
17790               _cimg_mp_check_type(arg2,2,1,0);
17791               _cimg_mp_check_type(arg3,3,1,0);
17792               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_cut,arg1,arg2,arg3);
17793               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) {
17794                 val = mem[arg1];
17795                 val1 = mem[arg2];
17796                 val2 = mem[arg3];
17797                 _cimg_mp_constant(val<val1?val1:val>val2?val2:val);
17798               }
17799               _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3);
17800             }
17801             break;
17802 
17803           case 'd' :
17804             if (*ss1=='(') { // Image depth
17805               _cimg_mp_op("Function 'd()'");
17806               if (*ss2=='#') { // Index specified
17807                 p1 = compile(ss3,se1,depth1,0,is_single);
17808                 _cimg_mp_check_list(false);
17809               } else { if (ss2!=se1) break; p1 = ~0U; }
17810               pos = scalar();
17811               CImg<ulongT>::vector((ulongT)mp_image_d,pos,p1).move_to(code);
17812               _cimg_mp_return(pos);
17813             }
17814 
17815             if (!std::strncmp(ss,"date(",5)) { // Current date or file date
17816               _cimg_mp_op("Function 'date()'");
17817               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17818               arg1 = ss5!=se1?compile(ss5,s1,depth1,0,is_single):~0U;
17819               is_sth = s1++!=se1; // is_filename
17820               pos = arg1==~0U || _cimg_mp_is_vector(arg1)?vector(arg1==~0U?7:_cimg_mp_size(arg1)):scalar();
17821               if (is_sth) {
17822                 *se1 = 0;
17823                 variable_name.assign(CImg<charT>::string(s1,true,true).unroll('y'),true);
17824                 cimg::strpare(variable_name,false,true);
17825                 ((CImg<ulongT>::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)),variable_name)>'y').
17826                   move_to(opcode);
17827                 *se1 = ')';
17828               } else
17829                 CImg<ulongT>::vector((ulongT)mp_date,pos,0,arg1,_cimg_mp_size(pos)).move_to(opcode);
17830               opcode[2] = opcode._height;
17831               opcode.move_to(code);
17832               _cimg_mp_return(pos);
17833             }
17834 
17835             if (!std::strncmp(ss,"debug(",6)) { // Print debug info
17836               _cimg_mp_op("Function 'debug()'");
17837               p1 = code._width;
17838               arg1 = compile(ss6,se1,depth1,p_ref,is_single);
17839               *se1 = 0;
17840               variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
17841               cimg::strpare(variable_name,false,true);
17842               ((CImg<ulongT>::vector((ulongT)mp_debug,arg1,0,code._width - p1),
17843                 variable_name)>'y').move_to(opcode);
17844               opcode[2] = opcode._height;
17845               opcode.move_to(code,p1);
17846               *se1 = ')';
17847               _cimg_mp_return(arg1);
17848             }
17849 
17850             if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image
17851               _cimg_mp_op("Function 'display()'");
17852               if (pexpr[se2 - expr._data]=='(') { // no arguments?
17853                 CImg<ulongT>::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code);
17854                 _cimg_mp_return_nan();
17855               }
17856               if (*ss8!='#') { // Vector
17857                 s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17858                 arg1 = compile(ss8,s1,depth1,0,is_single);
17859                 arg2 = 0; arg3 = arg4 = arg5 = 1;
17860                 if (s1<se1) {
17861                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17862                   arg2 = compile(s1 + 1,s2,depth1,0,is_single);
17863                   if (s2<se1) {
17864                     s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
17865                     arg3 = compile(s2,s3,depth1,0,is_single);
17866                     if (s3<se1) {
17867                       s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
17868                       arg4 = compile(s3,s2,depth1,0,is_single);
17869                       arg5 = s2<se1?compile(++s2,se1,depth1,0,is_single):0;
17870                     }
17871                   }
17872                 }
17873                 _cimg_mp_check_type(arg2,2,1,0);
17874                 _cimg_mp_check_type(arg3,3,1,0);
17875                 _cimg_mp_check_type(arg4,4,1,0);
17876                 _cimg_mp_check_type(arg5,5,1,0);
17877 
17878                 c1 = *s1; *s1 = 0;
17879                 variable_name.assign(CImg<charT>::string(ss8,true,true).unroll('y'),true);
17880                 cimg::strpare(variable_name,false,true);
17881                 if (_cimg_mp_is_vector(arg1))
17882                   ((CImg<ulongT>::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0),
17883                     variable_name)>'y').move_to(opcode);
17884                 else
17885                   ((CImg<ulongT>::vector((ulongT)mp_print,arg1,0,0),
17886                     variable_name)>'y').move_to(opcode);
17887                 opcode[2] = opcode._height;
17888                 opcode.move_to(code);
17889 
17890                 ((CImg<ulongT>::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1),
17891                                        arg2,arg3,arg4,arg5),
17892                   variable_name)>'y').move_to(opcode);
17893                 opcode[2] = opcode._height;
17894                 opcode.move_to(code);
17895                 *s1 = c1;
17896                 _cimg_mp_return(arg1);
17897 
17898               } else { // Image
17899                 p1 = compile(ss8 + 1,se1,depth1,0,is_single);
17900                 _cimg_mp_check_list(true);
17901                 CImg<ulongT>::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code);
17902                 _cimg_mp_return_nan();
17903               }
17904             }
17905 
17906             if (!std::strncmp(ss,"det(",4)) { // Matrix determinant
17907               _cimg_mp_op("Function 'det()'");
17908               arg1 = compile(ss4,se1,depth1,0,is_single);
17909               _cimg_mp_check_matrix_square(arg1,1);
17910               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
17911               _cimg_mp_scalar2(mp_det,arg1,p1);
17912             }
17913 
17914             if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix
17915               _cimg_mp_op("Function 'diag()'");
17916               arg1 = compile(ss5,se1,depth1,0,is_single);
17917               if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(arg1);
17918               p1 = _cimg_mp_size(arg1);
17919               pos = vector(p1*p1);
17920               CImg<ulongT>::vector((ulongT)mp_diag,pos,arg1,p1).move_to(code);
17921               _cimg_mp_return(pos);
17922             }
17923 
17924             if (!std::strncmp(ss,"dot(",4)) { // Dot product
17925               _cimg_mp_op("Function 'dot()'");
17926               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17927               arg1 = compile(ss4,s1,depth1,0,is_single);
17928               arg2 = compile(++s1,se1,depth1,0,is_single);
17929               _cimg_mp_check_type(arg1,1,2,0);
17930               _cimg_mp_check_type(arg2,2,2,0);
17931               if (_cimg_mp_is_vector(arg1)) _cimg_mp_scalar3(mp_dot,arg1,arg2,_cimg_mp_size(arg1));
17932               _cimg_mp_scalar2(mp_mul,arg1,arg2);
17933             }
17934 
17935             if (!std::strncmp(ss,"do(",3) || !std::strncmp(ss,"dowhile(",8)) { // Do..while
17936               _cimg_mp_op("Function 'dowhile()'");
17937               s0 = *ss2=='('?ss3:ss8;
17938               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17939               arg1 = code._width;
17940               arg6 = mempos;
17941               p1 = compile(s0,s1,depth1,0,is_single); // Body
17942               arg2 = code._width;
17943               p2 = s1<se1?compile(++s1,se1,depth1,0,is_single):p1; // Condition
17944               _cimg_mp_check_type(p2,2,1,0);
17945               CImg<ulongT>::vector((ulongT)mp_dowhile,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1),
17946                                    p1>=arg6 && !_cimg_mp_is_constant(p1),
17947                                    p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
17948               _cimg_mp_return(p1);
17949             }
17950 
17951             if (!std::strncmp(ss,"draw(",5)) { // Draw image
17952               if (!is_single) is_parallelizable = false;
17953               _cimg_mp_op("Function 'draw()'");
17954               if (*ss5=='#') { // Index specified
17955                 s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17956                 p1 = compile(ss6,s0++,depth1,0,is_single);
17957                 _cimg_mp_check_list(true);
17958               } else { p1 = ~0U; s0 = ss5; }
17959               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17960               arg1 = compile(s0,s1,depth1,0,is_single);
17961               arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
17962               arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
17963               arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
17964               arg5 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
17965               s0 = se1;
17966               if (s1<se1) {
17967                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17968                 arg2 = compile(++s1,s0,depth1,0,is_single);
17969                 if (_cimg_mp_is_vector(arg2)) { // Coordinates specified as a vector
17970                   p2 = _cimg_mp_size(arg2);
17971                   ++arg2;
17972                   if (p2>1) {
17973                     arg3 = arg2 + 1;
17974                     if (p2>2) {
17975                       arg4 = arg3 + 1;
17976                       if (p2>3) arg5 = arg4 + 1;
17977                     }
17978                   }
17979                   ++s0;
17980                   is_sth = true;
17981                 } else {
17982                   if (s0<se1) {
17983                     is_sth = p1!=~0U;
17984                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17985                     arg3 = compile(++s0,s1,depth1,0,is_single);
17986                     _cimg_mp_check_type(arg3,is_sth?4:3,1,0);
17987                     if (s1<se1) {
17988                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
17989                       arg4 = compile(++s1,s0,depth1,0,is_single);
17990                       _cimg_mp_check_type(arg4,is_sth?5:4,1,0);
17991                       if (s0<se1) {
17992                         s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
17993                         arg5 = compile(++s0,s1,depth1,0,is_single);
17994                         _cimg_mp_check_type(arg5,is_sth?6:5,1,0);
17995                         s0 = ++s1;
17996                       }
17997                     }
17998                   }
17999                   is_sth = false;
18000                 }
18001               }
18002 
18003               l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'!
18004               CImg<ulongT>::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5,
18005                                    0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode);
18006 
18007               arg2 = arg3 = arg4 = arg5 = ~0U;
18008               p2 = p1!=~0U?0:1;
18009               if (s0<se1) {
18010                 s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18011                 arg2 = compile(s0,s1,depth1,0,is_single);
18012                 _cimg_mp_check_type(arg2,p2 + (is_sth?3:6),1,0);
18013                 if (s1<se1) {
18014                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18015                   arg3 = compile(++s1,s0,depth1,0,is_single);
18016                   _cimg_mp_check_type(arg3,p2 + (is_sth?4:7),1,0);
18017                   if (s0<se1) {
18018                     s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18019                     arg4 = compile(++s0,s1,depth1,0,is_single);
18020                     _cimg_mp_check_type(arg4,p2 + (is_sth?5:8),1,0);
18021                     if (s1<se1) {
18022                       s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18023                       arg5 = compile(++s1,s0,depth1,0,is_single);
18024                       _cimg_mp_check_type(arg5,p2 + (is_sth?6:9),1,0);
18025                     }
18026                   }
18027                 }
18028               }
18029               if (s0<s1) s0 = s1;
18030 
18031               l_opcode(0,8) = (ulongT)arg2;
18032               l_opcode(0,9) = (ulongT)arg3;
18033               l_opcode(0,10) = (ulongT)arg4;
18034               l_opcode(0,11) = (ulongT)arg5;
18035 
18036               if (s0<se1) {
18037                 s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18038                 arg6 = compile(++s0,s1,depth1,0,is_single);
18039                 _cimg_mp_check_type(arg6,0,1,0);
18040                 l_opcode(0,12) = arg6;
18041                 if (s1<se1) {
18042                   s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18043                   p2 = compile(++s1,s0,depth1,0,is_single);
18044                   _cimg_mp_check_type(p2,0,2,0);
18045                   l_opcode(0,13) = p2;
18046                   l_opcode(0,14) = _cimg_mp_size(p2);
18047                   p3 = s0<se1?compile(++s0,se1,depth1,0,is_single):1;
18048                   _cimg_mp_check_type(p3,0,1,0);
18049                   l_opcode(0,15) = p3;
18050                 }
18051               }
18052               l_opcode[0].move_to(code);
18053               _cimg_mp_return(arg1);
18054             }
18055 
18056             break;
18057 
18058           case 'e' :
18059             if (!std::strncmp(ss,"echo(",5)) { // Echo
18060               _cimg_mp_op("Function 'echo()'");
18061               CImg<ulongT>::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode);
18062               for (s = ss5; s<se; ++s) {
18063                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18064                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18065                 arg1 = compile(s,ns,depth1,0,is_single);
18066                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
18067                 s = ns;
18068               }
18069               (l_opcode>'y').move_to(opcode);
18070               opcode[2] = opcode._height;
18071               opcode.move_to(code);
18072               _cimg_mp_return_nan();
18073             }
18074 
18075             if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector
18076               _cimg_mp_op("Function 'eig()'");
18077               arg1 = compile(ss4,se1,depth1,0,is_single);
18078               _cimg_mp_check_matrix_square(arg1,1);
18079               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
18080               pos = vector((p1 + 1)*p1);
18081               CImg<ulongT>::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code);
18082               _cimg_mp_return(pos);
18083             }
18084 
18085             if (!std::strncmp(ss,"end(",4)) { // End
18086               _cimg_mp_op("Function 'end()'");
18087               code.swap(code_end);
18088               compile(ss4,se1,depth1,p_ref,true);
18089               code.swap(code_end);
18090               _cimg_mp_return_nan();
18091             }
18092 
18093             if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing
18094               if (!is_single) is_parallelizable = false;
18095               _cimg_mp_op("Function 'ellipse()'");
18096               if (*ss8=='#') { // Index specified
18097                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18098                 p1 = compile(ss + 9,s0++,depth1,0,is_single);
18099                 _cimg_mp_check_list(true);
18100               } else { p1 = ~0U; s0 = ss8; }
18101               if (s0==se1) compile(s0,se1,depth1,0,is_single); // 'missing' argument error
18102               CImg<ulongT>::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
18103               for (s = s0; s<se; ++s) {
18104                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18105                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18106                 arg2 = compile(s,ns,depth1,0,is_single);
18107                 if (_cimg_mp_is_vector(arg2))
18108                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
18109                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
18110                     move_to(l_opcode);
18111                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
18112                 s = ns;
18113               }
18114               (l_opcode>'y').move_to(opcode);
18115               opcode[2] = opcode._height;
18116               opcode.move_to(code);
18117               _cimg_mp_return_nan();
18118             }
18119 
18120             if (!std::strncmp(ss,"ext(",4)) { // Extern
18121               _cimg_mp_op("Function 'ext()'");
18122               if (!is_single) is_parallelizable = false;
18123               CImg<ulongT>::vector((ulongT)mp_ext,0,0).move_to(l_opcode);
18124               pos = 1;
18125               for (s = ss4; s<se; ++s) {
18126                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18127                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18128                 arg1 = compile(s,ns,depth1,0,is_single);
18129                 CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
18130                 s = ns;
18131               }
18132               (l_opcode>'y').move_to(opcode);
18133               pos = scalar();
18134               opcode[1] = pos;
18135               opcode[2] = opcode._height;
18136               opcode.move_to(code);
18137               _cimg_mp_return(pos);
18138             }
18139 
18140             if (!std::strncmp(ss,"exp(",4)) { // Exponential
18141               _cimg_mp_op("Function 'exp()'");
18142               arg1 = compile(ss4,se1,depth1,0,is_single);
18143               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1);
18144               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1]));
18145               _cimg_mp_scalar1(mp_exp,arg1);
18146             }
18147 
18148             if (!std::strncmp(ss,"eye(",4)) { // Identity matrix
18149               _cimg_mp_op("Function 'eye()'");
18150               arg1 = compile(ss4,se1,depth1,0,is_single);
18151               _cimg_mp_check_constant(arg1,1,3);
18152               p1 = (unsigned int)mem[arg1];
18153               pos = vector(p1*p1);
18154               CImg<ulongT>::vector((ulongT)mp_eye,pos,p1).move_to(code);
18155               _cimg_mp_return(pos);
18156             }
18157             break;
18158 
18159           case 'f' :
18160             if (!std::strncmp(ss,"fact(",5)) { // Factorial
18161               _cimg_mp_op("Function 'fact()'");
18162               arg1 = compile(ss5,se1,depth1,0,is_single);
18163               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1);
18164               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1]));
18165               _cimg_mp_scalar1(mp_factorial,arg1);
18166             }
18167 
18168             if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci
18169               _cimg_mp_op("Function 'fibo()'");
18170               arg1 = compile(ss5,se1,depth1,0,is_single);
18171               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1);
18172               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1]));
18173               _cimg_mp_scalar1(mp_fibonacci,arg1);
18174             }
18175 
18176             if (!std::strncmp(ss,"find(",5)) { // Find
18177               _cimg_mp_op("Function 'find()'");
18178 
18179               // First argument: data to look at.
18180               s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18181               if (*ss5=='#') { // Index specified
18182                 p1 = compile(ss6,s0,depth1,0,is_single);
18183                 _cimg_mp_check_list(false);
18184                 arg1 = ~0U;
18185               } else { // Vector specified
18186                 arg1 = compile(ss5,s0,depth1,0,is_single);
18187                 _cimg_mp_check_type(arg1,1,2,0);
18188                 p1 = ~0U;
18189               }
18190 
18191               // Second argument: data to find.
18192               s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18193               arg2 = compile(s0,s1,depth1,0,is_single);
18194 
18195               // Third and fourth arguments: search direction and starting index.
18196               arg3 = 1; arg4 = _cimg_mp_slot_nan;
18197               if (s1<se1) {
18198                 s0 = s1 + 1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18199                 arg3 = compile(++s1,s0,depth1,0,is_single);
18200                 _cimg_mp_check_type(arg3,3,1,0);
18201                 if (s0<se1) {
18202                   arg4 = compile(++s0,se1,depth1,0,is_single);
18203                   _cimg_mp_check_type(arg4,4,1,0);
18204                 }
18205               }
18206               if (p1!=~0U) {
18207                 if (_cimg_mp_is_vector(arg2))
18208                   _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4);
18209                 _cimg_mp_scalar4(mp_list_find,p1,arg2,arg3,arg4);
18210               }
18211               if (_cimg_mp_is_vector(arg2))
18212                 _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4);
18213               _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2,arg3,arg4);
18214             }
18215 
18216             if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop
18217               _cimg_mp_op("Function 'for()'");
18218               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18219               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18220               s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18221               arg1 = code._width;
18222               p1 = compile(ss4,s1,depth1,0,is_single); // Init
18223               arg2 = code._width;
18224               p2 = compile(++s1,s2,depth1,0,is_single); // Cond
18225               arg3 = code._width;
18226               arg6 = mempos;
18227               if (s3<se1) { // Body + post
18228                 p3 = compile(s3 + 1,se1,depth1,0,is_single); // Body
18229                 arg4 = code._width;
18230                 pos = compile(++s2,s3,depth1,0,is_single); // Post
18231               } else {
18232                 p3 = compile(++s2,se1,depth1,0,is_single); // Body only
18233                 arg4 = pos = code._width;
18234               }
18235               _cimg_mp_check_type(p2,2,1,0);
18236               arg5 = _cimg_mp_size(pos);
18237               CImg<ulongT>::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2,
18238                                    arg4 - arg3,code._width - arg4,
18239                                    p3>=arg6 && !_cimg_mp_is_constant(p3),
18240                                    p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1);
18241               _cimg_mp_return(p3);
18242             }
18243 
18244             if (!std::strncmp(ss,"floor(",6)) { // Floor
18245               _cimg_mp_op("Function 'floor()'");
18246               arg1 = compile(ss6,se1,depth1,0,is_single);
18247               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1);
18248               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1]));
18249               _cimg_mp_scalar1(mp_floor,arg1);
18250             }
18251 
18252             if (!std::strncmp(ss,"fsize(",6)) { // File size
18253               _cimg_mp_op("Function 'fsize()'");
18254               *se1 = 0;
18255               variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
18256               cimg::strpare(variable_name,false,true);
18257               pos = scalar();
18258               ((CImg<ulongT>::vector((ulongT)mp_fsize,pos,0),variable_name)>'y').move_to(opcode);
18259               *se1 = ')';
18260               opcode[2] = opcode._height;
18261               opcode.move_to(code);
18262               _cimg_mp_return(pos);
18263             }
18264             break;
18265 
18266           case 'g' :
18267             if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function
18268               _cimg_mp_op("Function 'gauss()'");
18269               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18270               arg1 = compile(ss6,s1,depth1,0,is_single);
18271               arg2 = arg3 = 1;
18272               if (s1<se1) {
18273                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18274                 arg2 = compile(++s1,s2,depth1,0,is_single);
18275                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
18276               }
18277               _cimg_mp_check_type(arg2,2,1,0);
18278               _cimg_mp_check_type(arg3,3,1,0);
18279               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_gauss,arg1,arg2,arg3);
18280               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3)) {
18281                 val1 = mem[arg1];
18282                 val2 = mem[arg2];
18283                 _cimg_mp_constant(std::exp(-val1*val1/(2*val2*val2))/(mem[arg3]?std::sqrt(2*val2*val2*cimg::PI):1));
18284               }
18285               _cimg_mp_scalar3(mp_gauss,arg1,arg2,arg3);
18286             }
18287 
18288             if (!std::strncmp(ss,"gcd(",4)) { // Gcd
18289               _cimg_mp_op("Function 'gcd()'");
18290               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18291               arg1 = compile(ss4,s1,depth1,0,is_single);
18292               arg2 = compile(++s1,se1,depth1,0,is_single);
18293               _cimg_mp_check_type(arg1,1,1,0);
18294               _cimg_mp_check_type(arg2,2,1,0);
18295               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18296                 _cimg_mp_constant(cimg::gcd((long)mem[arg1],(long)mem[arg2]));
18297               _cimg_mp_scalar2(mp_gcd,arg1,arg2);
18298             }
18299             break;
18300 
18301           case 'h' :
18302             if (*ss1=='(') { // Image height
18303               _cimg_mp_op("Function 'h()'");
18304               if (*ss2=='#') { // Index specified
18305                 p1 = compile(ss3,se1,depth1,0,is_single);
18306                 _cimg_mp_check_list(false);
18307               } else { if (ss2!=se1) break; p1 = ~0U; }
18308               pos = scalar();
18309               CImg<ulongT>::vector((ulongT)mp_image_h,pos,p1).move_to(code);
18310               _cimg_mp_return(pos);
18311             }
18312             break;
18313 
18314           case 'i' :
18315             if (*ss1=='c' && *ss2=='(') { // Image median
18316               _cimg_mp_op("Function 'ic()'");
18317               if (*ss3=='#') { // Index specified
18318                 p1 = compile(ss4,se1,depth1,0,is_single);
18319                 _cimg_mp_check_list(false);
18320               } else { if (ss3!=se1) break; p1 = ~0U; }
18321               pos = scalar();
18322               CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code);
18323               _cimg_mp_return(pos);
18324             }
18325 
18326             if (*ss1=='f' && *ss2=='(') { // If..then[..else.]
18327               _cimg_mp_op("Function 'if()'");
18328               s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18329               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18330               arg1 = compile(ss3,s1,depth1,0,is_single);
18331               _cimg_mp_check_type(arg1,1,1,0);
18332               if (_cimg_mp_is_constant(arg1)) {
18333                 if ((bool)mem[arg1]) return compile(++s1,s2,depth1,0,is_single);
18334                 else return s2<se1?compile(++s2,se1,depth1,0,is_single):0;
18335               }
18336               p2 = code._width;
18337               arg2 = compile(++s1,s2,depth1,0,is_single);
18338               p3 = code._width;
18339               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):
18340                 _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
18341               _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
18342               arg4 = _cimg_mp_size(arg2);
18343               if (arg4) pos = vector(arg4); else pos = scalar();
18344               CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
18345                                   p3 - p2,code._width - p3,arg4).move_to(code,p2);
18346               _cimg_mp_return(pos);
18347             }
18348 
18349             if (!std::strncmp(ss,"init(",5)) { // Init
18350               _cimg_mp_op("Function 'init()'");
18351               code.swap(code_init);
18352               arg1 = compile(ss5,se1,depth1,p_ref,true);
18353               code.swap(code_init);
18354               _cimg_mp_return(arg1);
18355             }
18356 
18357             if (!std::strncmp(ss,"int(",4)) { // Integer cast
18358               _cimg_mp_op("Function 'int()'");
18359               arg1 = compile(ss4,se1,depth1,0,is_single);
18360               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1);
18361               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]);
18362               _cimg_mp_scalar1(mp_int,arg1);
18363             }
18364 
18365             if (!std::strncmp(ss,"inv(",4)) { // Matrix/scalar inversion
18366               _cimg_mp_op("Function 'inv()'");
18367               arg1 = compile(ss4,se1,depth1,0,is_single);
18368               if (_cimg_mp_is_vector(arg1)) {
18369                 _cimg_mp_check_matrix_square(arg1,1);
18370                 p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
18371                 pos = vector(p1*p1);
18372                 CImg<ulongT>::vector((ulongT)mp_matrix_inv,pos,arg1,p1).move_to(code);
18373                 _cimg_mp_return(pos);
18374               }
18375               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]);
18376               _cimg_mp_scalar2(mp_div,1,arg1);
18377             }
18378 
18379             if (*ss1=='s') { // Family of 'is_?()' functions
18380 
18381               if (!std::strncmp(ss,"isbool(",7)) { // Is boolean?
18382                 _cimg_mp_op("Function 'isbool()'");
18383                 if (ss7==se1) _cimg_mp_return(0);
18384                 arg1 = compile(ss7,se1,depth1,0,is_single);
18385                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1);
18386                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0.0 || mem[arg1]==1.0);
18387                 _cimg_mp_scalar1(mp_isbool,arg1);
18388               }
18389 
18390               if (!std::strncmp(ss,"isdir(",6)) { // Is directory?
18391                 _cimg_mp_op("Function 'isdir()'");
18392                 *se1 = 0;
18393                 is_sth = cimg::is_directory(ss6);
18394                 *se1 = ')';
18395                 _cimg_mp_return(is_sth?1U:0U);
18396               }
18397 
18398               if (!std::strncmp(ss,"isfile(",7)) { // Is file?
18399                 _cimg_mp_op("Function 'isfile()'");
18400                 *se1 = 0;
18401                 is_sth = cimg::is_file(ss7);
18402                 *se1 = ')';
18403                 _cimg_mp_return(is_sth?1U:0U);
18404               }
18405 
18406               if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector?
18407                 if (ss5>=se1) _cimg_mp_return(0);
18408                 _cimg_mp_op("Function 'isin()'");
18409                 pos = scalar();
18410                 CImg<ulongT>::vector((ulongT)mp_isin,pos,0).move_to(l_opcode);
18411                 for (s = ss5; s<se; ++s) {
18412                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18413                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18414                   arg1 = compile(s,ns,depth1,0,is_single);
18415                   if (_cimg_mp_is_vector(arg1))
18416                     CImg<ulongT>::sequence(_cimg_mp_size(arg1),arg1 + 1,
18417                                            arg1 + (ulongT)_cimg_mp_size(arg1)).
18418                       move_to(l_opcode);
18419                   else CImg<ulongT>::vector(arg1).move_to(l_opcode);
18420                   s = ns;
18421                 }
18422                 (l_opcode>'y').move_to(opcode);
18423                 opcode[2] = opcode._height;
18424                 opcode.move_to(code);
18425                 _cimg_mp_return(pos);
18426               }
18427 
18428               if (!std::strncmp(ss,"isinf(",6)) { // Is infinite?
18429                 _cimg_mp_op("Function 'isinf()'");
18430                 if (ss6==se1) _cimg_mp_return(0);
18431                 arg1 = compile(ss6,se1,depth1,0,is_single);
18432                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1);
18433                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_inf(mem[arg1]));
18434                 _cimg_mp_scalar1(mp_isinf,arg1);
18435               }
18436 
18437               if (!std::strncmp(ss,"isint(",6)) { // Is integer?
18438                 _cimg_mp_op("Function 'isint()'");
18439                 if (ss6==se1) _cimg_mp_return(0);
18440                 arg1 = compile(ss6,se1,depth1,0,is_single);
18441                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1);
18442                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)(cimg::mod(mem[arg1],1.0)==0));
18443                 _cimg_mp_scalar1(mp_isint,arg1);
18444               }
18445 
18446               if (!std::strncmp(ss,"isnan(",6)) { // Is NaN?
18447                 _cimg_mp_op("Function 'isnan()'");
18448                 if (ss6==se1) _cimg_mp_return(0);
18449                 arg1 = compile(ss6,se1,depth1,0,is_single);
18450                 if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1);
18451                 if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type<double>::is_nan(mem[arg1]));
18452                 _cimg_mp_scalar1(mp_isnan,arg1);
18453               }
18454 
18455               if (!std::strncmp(ss,"isval(",6)) { // Is value?
18456                 _cimg_mp_op("Function 'isval()'");
18457                 val = 0;
18458                 if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
18459                 _cimg_mp_return(0);
18460               }
18461 
18462             }
18463             break;
18464 
18465           case 'l' :
18466             if (*ss1=='(') { // Size of image list
18467               _cimg_mp_op("Function 'l()'");
18468               if (ss2!=se1) break;
18469               _cimg_mp_scalar0(mp_list_l);
18470             }
18471 
18472             if (!std::strncmp(ss,"log(",4)) { // Natural logarithm
18473               _cimg_mp_op("Function 'log()'");
18474               arg1 = compile(ss4,se1,depth1,0,is_single);
18475               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1);
18476               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1]));
18477               _cimg_mp_scalar1(mp_log,arg1);
18478             }
18479 
18480             if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm
18481               _cimg_mp_op("Function 'log2()'");
18482               arg1 = compile(ss5,se1,depth1,0,is_single);
18483               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1);
18484               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1]));
18485               _cimg_mp_scalar1(mp_log2,arg1);
18486             }
18487 
18488             if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm
18489               _cimg_mp_op("Function 'log10()'");
18490               arg1 = compile(ss6,se1,depth1,0,is_single);
18491               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1);
18492               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1]));
18493               _cimg_mp_scalar1(mp_log10,arg1);
18494             }
18495 
18496             if (!std::strncmp(ss,"lowercase(",10)) { // Lower case
18497               _cimg_mp_op("Function 'lowercase()'");
18498               arg1 = compile(ss + 10,se1,depth1,0,is_single);
18499               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1);
18500               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1]));
18501               _cimg_mp_scalar1(mp_lowercase,arg1);
18502             }
18503             break;
18504 
18505           case 'm' :
18506             if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication
18507               _cimg_mp_op("Function 'mul()'");
18508               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18509               arg1 = compile(ss4,s1,depth1,0,is_single);
18510               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18511               arg2 = compile(++s1,s2,depth1,0,is_single);
18512               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
18513               _cimg_mp_check_type(arg1,1,2,0);
18514               _cimg_mp_check_type(arg2,2,2,0);
18515               _cimg_mp_check_constant(arg3,3,3);
18516               p1 = _cimg_mp_size(arg1);
18517               p2 = _cimg_mp_size(arg2);
18518               p3 = (unsigned int)mem[arg3];
18519               arg5 = p2/p3;
18520               arg4 = p1/arg5;
18521               if (arg4*arg5!=p1 || arg5*p3!=p2) {
18522                 *se = saved_char;
18523                 s0 = ss - 4>expr._data?ss - 4:expr._data;
18524                 cimg::strellipsize(s0,64);
18525                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
18526                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
18527                                             "do not match with third argument 'nb_colsB=%u', "
18528                                             "in expression '%s%s%s'.",
18529                                             pixel_type(),_cimg_mp_calling_function,s_op,
18530                                             s_type(arg1)._data,s_type(arg2)._data,p3,
18531                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18532               }
18533               pos = vector(arg4*p3);
18534               CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
18535               _cimg_mp_return(pos);
18536             }
18537             break;
18538 
18539           case 'n' :
18540             if (!std::strncmp(ss,"narg(",5)) { // Number of arguments
18541               _cimg_mp_op("Function 'narg()'");
18542               if (ss5>=se1) _cimg_mp_return(0);
18543               arg1 = 0;
18544               for (s = ss5; s<se; ++s) {
18545                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18546                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18547                 ++arg1; s = ns;
18548               }
18549               _cimg_mp_constant(arg1);
18550             }
18551 
18552             if ((cimg_sscanf(ss,"norm%u%c",&(arg1=~0U),&sep)==2 && sep=='(') ||
18553                 !std::strncmp(ss,"norminf(",8) || !std::strncmp(ss,"norm(",5) ||
18554                 (!std::strncmp(ss,"norm",4) && ss5<se1 && (s=std::strchr(ss5,'('))!=0)) { // Lp norm
18555               _cimg_mp_op("Function 'normP()'");
18556               if (*ss4=='(') { arg1 = 2; s = ss5; }
18557               else if (*ss4=='i' && *ss5=='n' && *ss6=='f' && *ss7=='(') { arg1 = ~0U; s = ss8; }
18558               else if (arg1==~0U) {
18559                 arg1 = compile(ss4,s++,depth1,0,is_single);
18560                 _cimg_mp_check_constant(arg1,0,2);
18561                 arg1 = (unsigned int)mem[arg1];
18562               } else s = std::strchr(ss4,'(') + 1;
18563               pos = scalar();
18564               switch (arg1) {
18565               case 0 :
18566                 CImg<ulongT>::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break;
18567               case 1 :
18568                 CImg<ulongT>::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break;
18569               case 2 :
18570                 CImg<ulongT>::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break;
18571               case ~0U :
18572                 CImg<ulongT>::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break;
18573               default :
18574                 CImg<ulongT>::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)).
18575                   move_to(l_opcode);
18576               }
18577               for ( ; s<se; ++s) {
18578                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18579                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18580                 arg2 = compile(s,ns,depth1,0,is_single);
18581                 if (_cimg_mp_is_vector(arg2))
18582                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
18583                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
18584                     move_to(l_opcode);
18585                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
18586                 s = ns;
18587               }
18588 
18589               (l_opcode>'y').move_to(opcode);
18590               if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1
18591                 _cimg_mp_scalar1(mp_abs,opcode[3]);
18592               opcode[2] = opcode._height;
18593               opcode.move_to(code);
18594               _cimg_mp_return(pos);
18595             }
18596             break;
18597 
18598           case 'p' :
18599             if (!std::strncmp(ss,"permut(",7)) { // Number of permutations
18600               _cimg_mp_op("Function 'permut()'");
18601               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18602               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18603               arg1 = compile(ss7,s1,depth1,0,is_single);
18604               arg2 = compile(++s1,s2,depth1,0,is_single);
18605               arg3 = compile(++s2,se1,depth1,0,is_single);
18606               _cimg_mp_check_type(arg1,1,1,0);
18607               _cimg_mp_check_type(arg2,2,1,0);
18608               _cimg_mp_check_type(arg3,3,1,0);
18609               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
18610                 _cimg_mp_constant(cimg::permutations((int)mem[arg1],(int)mem[arg2],(bool)mem[arg3]));
18611               _cimg_mp_scalar3(mp_permutations,arg1,arg2,arg3);
18612             }
18613 
18614             if (!std::strncmp(ss,"polygon(",8)) { // Polygon/line drawing
18615               if (!is_single) is_parallelizable = false;
18616               _cimg_mp_op("Function 'polygon()'");
18617               if (*ss8=='#') { // Index specified
18618                 s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18619                 p1 = compile(ss + 9,s0++,depth1,0,is_single);
18620                 _cimg_mp_check_list(true);
18621               } else { p1 = ~0U; s0 = ss8; }
18622               if (s0==se1) compile(s0,se1,depth1,0,is_single); // 'missing' argument error
18623               CImg<ulongT>::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
18624               for (s = s0; s<se; ++s) {
18625                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18626                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18627                 arg2 = compile(s,ns,depth1,0,is_single);
18628                 if (_cimg_mp_is_vector(arg2))
18629                   CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
18630                                          arg2 + (ulongT)_cimg_mp_size(arg2)).
18631                     move_to(l_opcode);
18632                 else CImg<ulongT>::vector(arg2).move_to(l_opcode);
18633                 s = ns;
18634               }
18635               (l_opcode>'y').move_to(opcode);
18636               opcode[2] = opcode._height;
18637               opcode.move_to(code);
18638               _cimg_mp_return_nan();
18639             }
18640 
18641             if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions
18642               is_sth = ss[5]=='s'; // is prints()
18643               _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'");
18644               s0 = is_sth?ss7:ss6;
18645               if (*s0!='#' || is_sth) { // Regular expression
18646                 for (s = s0; s<se; ++s) {
18647                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18648                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18649                   pos = compile(s,ns,depth1,p_ref,is_single);
18650                   c1 = *ns; *ns = 0;
18651                   variable_name.assign(CImg<charT>::string(s,true,true).unroll('y'),true);
18652                   cimg::strpare(variable_name,false,true);
18653                   if (_cimg_mp_is_vector(pos)) // Vector
18654                     ((CImg<ulongT>::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0),
18655                       variable_name)>'y').move_to(opcode);
18656                   else // Scalar
18657                     ((CImg<ulongT>::vector((ulongT)mp_print,pos,0,is_sth?1:0),
18658                       variable_name)>'y').move_to(opcode);
18659                   opcode[2] = opcode._height;
18660                   opcode.move_to(code);
18661                   *ns = c1; s = ns;
18662                 }
18663                 _cimg_mp_return(pos);
18664               } else { // Image
18665                 p1 = compile(ss7,se1,depth1,0,is_single);
18666                 _cimg_mp_check_list(true);
18667                 CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code);
18668                 _cimg_mp_return_nan();
18669               }
18670             }
18671 
18672             if (!std::strncmp(ss,"pseudoinv(",10)) { // Matrix/scalar pseudo-inversion
18673               _cimg_mp_op("Function 'pseudoinv()'");
18674               s1 = ss + 10; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18675               arg1 = compile(ss + 10,s1,depth1,0,is_single);
18676               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
18677               _cimg_mp_check_type(arg1,1,2,0);
18678               _cimg_mp_check_constant(arg2,2,3);
18679               p1 = _cimg_mp_size(arg1);
18680               p2 = (unsigned int)mem[arg2];
18681               p3 = p1/p2;
18682               if (p3*p2!=p1) {
18683                 *se = saved_char;
18684                 s0 = ss - 4>expr._data?ss - 4:expr._data;
18685                 cimg::strellipsize(s0,64);
18686                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
18687                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
18688                                             "does not match with second argument 'nb_colsA=%u', "
18689                                             "in expression '%s%s%s'.",
18690                                             pixel_type(),_cimg_mp_calling_function,s_op,
18691                                             s_type(arg1)._data,p2,
18692                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18693               }
18694               pos = vector(p1);
18695               CImg<ulongT>::vector((ulongT)mp_matrix_pseudoinv,pos,arg1,p2,p3).move_to(code);
18696               _cimg_mp_return(pos);
18697             }
18698             break;
18699 
18700           case 'r' :
18701             if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize
18702               _cimg_mp_op("Function 'resize()'");
18703               if (*ss7!='#') { // Vector
18704                 s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18705                 arg1 = compile(ss7,s1,depth1,0,is_single);
18706                 s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18707                 arg2 = compile(s1,s2,depth1,0,is_single);
18708                 arg3 = 1;
18709                 arg4 = 0;
18710                 if (s2<se1) {
18711                   s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18712                   arg3 = compile(s2,s1,depth1,0,is_single);
18713                   arg4 = s1<se1?compile(++s1,se1,depth1,0,is_single):0;
18714                 }
18715                 _cimg_mp_check_constant(arg2,2,3);
18716                 arg2 = (unsigned int)mem[arg2];
18717                 _cimg_mp_check_type(arg3,3,1,0);
18718                 _cimg_mp_check_type(arg4,4,1,0);
18719                 pos = vector(arg2);
18720                 CImg<ulongT>::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1),
18721                                      arg3,arg4).move_to(code);
18722                 _cimg_mp_return(pos);
18723 
18724               } else { // Image
18725                 if (!is_single) is_parallelizable = false;
18726                 s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18727                 p1 = compile(ss8,s0++,depth1,0,is_single);
18728                 _cimg_mp_check_list(true);
18729                 l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'!
18730                 CImg<ulongT>::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0).
18731                   move_to(l_opcode);
18732                 pos = 0;
18733                 for (s = s0; s<se && pos<10; ++s) {
18734                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
18735                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
18736                   arg1 = compile(s,ns,depth1,0,is_single);
18737                   _cimg_mp_check_type(arg1,pos + 2,1,0);
18738                   l_opcode(0,pos + 3) = arg1;
18739                   s = ns;
18740                   ++pos;
18741                 }
18742                 if (pos<1 || pos>10) {
18743                   *se = saved_char;
18744                   s0 = ss - 4>expr._data?ss - 4:expr._data;
18745                   cimg::strellipsize(s0,64);
18746                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
18747                                               "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.",
18748                                               pixel_type(),_cimg_mp_calling_function,s_op,
18749                                               pos<1?"Missing":"Too much",
18750                                               s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18751                 }
18752                 l_opcode[0].move_to(code);
18753                 _cimg_mp_return_nan();
18754               }
18755             }
18756 
18757             if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse
18758               _cimg_mp_op("Function 'reverse()'");
18759               arg1 = compile(ss8,se1,depth1,0,is_single);
18760               if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1);
18761               p1 = _cimg_mp_size(arg1);
18762               pos = vector(p1);
18763               CImg<ulongT>::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code);
18764               _cimg_mp_return(pos);
18765             }
18766 
18767             if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation
18768               _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'");
18769               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
18770               arg1 = compile(ss4,s1,depth1,0,is_single);
18771               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
18772               _cimg_mp_check_type(arg2,2,1,0);
18773               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
18774               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
18775                 _cimg_mp_constant(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]):
18776                                   cimg::ror(mem[arg1],(unsigned int)mem[arg2]));
18777               _cimg_mp_scalar2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
18778             }
18779 
18780             if (!std::strncmp(ss,"rot(",4)) { // 2d/3d rotation matrix
18781               _cimg_mp_op("Function 'rot()'");
18782               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18783               arg1 = compile(ss4,s1,depth1,0,is_single);
18784               if (s1<se1) { // 3d rotation
18785                 _cimg_mp_check_type(arg1,1,3,3);
18786                 is_sth = false; // Is coordinates as vector?
18787                 if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
18788                   is_sth = true;
18789                   p2 = _cimg_mp_size(arg1);
18790                   ++arg1;
18791                   arg2 = arg3 = 0;
18792                   if (p2>1) {
18793                     arg2 = arg1 + 1;
18794                     if (p2>2) arg3 = arg2 + 1;
18795                   }
18796                   arg4 = compile(++s1,se1,depth1,0,is_single);
18797                 } else {
18798                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18799                   arg2 = compile(++s1,s2,depth1,0,is_single);
18800                   s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18801                   arg3 = compile(++s2,s3,depth1,0,is_single);
18802                   arg4 = compile(++s3,se1,depth1,0,is_single);
18803                   _cimg_mp_check_type(arg2,2,1,0);
18804                   _cimg_mp_check_type(arg3,3,1,0);
18805                 }
18806                 _cimg_mp_check_type(arg4,is_sth?2:4,1,0);
18807                 pos = vector(9);
18808                 CImg<ulongT>::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code);
18809               } else { // 2d rotation
18810                 _cimg_mp_check_type(arg1,1,1,0);
18811                 pos = vector(4);
18812                 CImg<ulongT>::vector((ulongT)mp_rot2d,pos,arg1).move_to(code);
18813               }
18814               _cimg_mp_return(pos);
18815             }
18816 
18817             if (!std::strncmp(ss,"round(",6)) { // Value rounding
18818               _cimg_mp_op("Function 'round()'");
18819               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18820               arg1 = compile(ss6,s1,depth1,0,is_single);
18821               arg2 = 1;
18822               arg3 = 0;
18823               if (s1<se1) {
18824                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18825                 arg2 = compile(++s1,s2,depth1,0,is_single);
18826                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):0;
18827               }
18828               _cimg_mp_check_type(arg2,2,1,0);
18829               _cimg_mp_check_type(arg3,3,1,0);
18830               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_round,arg1,arg2,arg3);
18831               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2) && _cimg_mp_is_constant(arg3))
18832                 _cimg_mp_constant(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3]));
18833               _cimg_mp_scalar3(mp_round,arg1,arg2,arg3);
18834             }
18835             break;
18836 
18837           case 's' :
18838             if (*ss1=='(') { // Image spectrum
18839               _cimg_mp_op("Function 's()'");
18840               if (*ss2=='#') { // Index specified
18841                 p1 = compile(ss3,se1,depth1,0,is_single);
18842                 _cimg_mp_check_list(false);
18843               } else { if (ss2!=se1) break; p1 = ~0U; }
18844               pos = scalar();
18845               CImg<ulongT>::vector((ulongT)mp_image_s,pos,p1).move_to(code);
18846               _cimg_mp_return(pos);
18847             }
18848 
18849             if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values
18850               _cimg_mp_op("Function 'same()'");
18851               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18852               arg1 = compile(ss5,s1,depth1,0,is_single);
18853               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18854               arg2 = compile(++s1,s2,depth1,0,is_single);
18855               arg3 = 11;
18856               arg4 = 1;
18857               if (s2<se1) {
18858                 s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
18859                 arg3 = compile(++s2,s3,depth1,0,is_single);
18860                 _cimg_mp_check_type(arg3,3,1,0);
18861                 arg4 = s3<se1?compile(++s3,se1,depth1,0,is_single):1;
18862               }
18863               p1 = _cimg_mp_size(arg1);
18864               p2 = _cimg_mp_size(arg2);
18865               _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,arg3,arg4);
18866             }
18867 
18868             if (!std::strncmp(ss,"shift(",6)) { // Shift vector
18869               _cimg_mp_op("Function 'shift()'");
18870               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18871               arg1 = compile(ss6,s1,depth1,0,is_single);
18872               arg2 = 1; arg3 = 0;
18873               if (s1<se1) {
18874                 s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18875                 arg2 = compile(s1,s0,depth1,0,is_single);
18876                 arg3 = s0<se1?compile(++s0,se1,depth1,0,is_single):0;
18877               }
18878               _cimg_mp_check_type(arg1,1,2,0);
18879               _cimg_mp_check_type(arg2,2,1,0);
18880               _cimg_mp_check_type(arg3,3,1,0);
18881               p1 = _cimg_mp_size(arg1);
18882               pos = vector(p1);
18883               CImg<ulongT>::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code);
18884               _cimg_mp_return(pos);
18885             }
18886 
18887             if (!std::strncmp(ss,"sign(",5)) { // Sign
18888               _cimg_mp_op("Function 'sign()'");
18889               arg1 = compile(ss5,se1,depth1,0,is_single);
18890               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1);
18891               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1]));
18892               _cimg_mp_scalar1(mp_sign,arg1);
18893             }
18894 
18895             if (!std::strncmp(ss,"sin(",4)) { // Sine
18896               _cimg_mp_op("Function 'sin()'");
18897               arg1 = compile(ss4,se1,depth1,0,is_single);
18898               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1);
18899               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1]));
18900               _cimg_mp_scalar1(mp_sin,arg1);
18901             }
18902 
18903             if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal
18904               _cimg_mp_op("Function 'sinc()'");
18905               arg1 = compile(ss5,se1,depth1,0,is_single);
18906               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1);
18907               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1]));
18908               _cimg_mp_scalar1(mp_sinc,arg1);
18909             }
18910 
18911             if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine
18912               _cimg_mp_op("Function 'sinh()'");
18913               arg1 = compile(ss5,se1,depth1,0,is_single);
18914               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1);
18915               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1]));
18916               _cimg_mp_scalar1(mp_sinh,arg1);
18917             }
18918 
18919             if (!std::strncmp(ss,"size(",5)) { // Vector size.
18920               _cimg_mp_op("Function 'size()'");
18921               arg1 = compile(ss5,se1,depth1,0,is_single);
18922               _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1));
18923             }
18924 
18925             if (!std::strncmp(ss,"solve(",6)) { // Solve linear system
18926               _cimg_mp_op("Function 'solve()'");
18927               s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18928               arg1 = compile(ss6,s1,depth1,0,is_single);
18929               s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18930               arg2 = compile(++s1,s2,depth1,0,is_single);
18931               arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):1;
18932               _cimg_mp_check_type(arg1,1,2,0);
18933               _cimg_mp_check_type(arg2,2,2,0);
18934               _cimg_mp_check_constant(arg3,3,3);
18935               p1 = _cimg_mp_size(arg1);
18936               p2 = _cimg_mp_size(arg2);
18937               p3 = (unsigned int)mem[arg3];
18938               arg5 = p2/p3;
18939               arg4 = p1/arg5;
18940               if (arg4*arg5!=p1 || arg5*p3!=p2) {
18941                 *se = saved_char;
18942                 s0 = ss - 4>expr._data?ss - 4:expr._data;
18943                 cimg::strellipsize(s0,64);
18944                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
18945                                             "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
18946                                             "do not match with third argument 'nb_colsB=%u', "
18947                                             "in expression '%s%s%s'.",
18948                                             pixel_type(),_cimg_mp_calling_function,s_op,
18949                                             s_type(arg1)._data,s_type(arg2)._data,p3,
18950                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18951               }
18952               pos = vector(arg4*p3);
18953               CImg<ulongT>::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
18954               _cimg_mp_return(pos);
18955             }
18956 
18957             if (!std::strncmp(ss,"sort(",5)) { // Sort vector
18958               _cimg_mp_op("Function 'sort()'");
18959               if (*ss5!='#') { // Vector
18960                 s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18961                 arg1 = compile(ss5,s1,depth1,0,is_single);
18962                 arg2 = arg3 = 1;
18963                 if (s1<se1) {
18964                   s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
18965                   arg2 = compile(s1,s0,depth1,0,is_single);
18966                   arg3 = s0<se1?compile(++s0,se1,depth1,0,is_single):1;
18967                 }
18968                 _cimg_mp_check_type(arg1,1,2,0);
18969                 _cimg_mp_check_type(arg2,2,1,0);
18970                 _cimg_mp_check_constant(arg3,3,3);
18971                 arg3 = (unsigned int)mem[arg3];
18972                 p1 = _cimg_mp_size(arg1);
18973                 if (p1%arg3) {
18974                   *se = saved_char;
18975                   s0 = ss - 4>expr._data?ss - 4:expr._data;
18976                   cimg::strellipsize(s0,64);
18977                   throw CImgArgumentException("[" cimg_appname "_math_parser] "
18978                                               "CImg<%s>::%s: %s: Invalid specified chunk size (%u) for first argument "
18979                                               "('%s'), in expression '%s%s%s'.",
18980                                               pixel_type(),_cimg_mp_calling_function,s_op,
18981                                               arg3,s_type(arg1)._data,
18982                                               s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
18983                 }
18984                 pos = vector(p1);
18985                 CImg<ulongT>::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3).move_to(code);
18986                 _cimg_mp_return(pos);
18987 
18988               } else { // Image
18989                 s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
18990                 p1 = compile(ss6,s1,depth1,0,is_single);
18991                 arg1 = 1;
18992                 arg2 = constant(-1.0);
18993                 if (s1<se1) {
18994                   s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
18995                   arg1 = compile(++s1,s2,depth1,0,is_single);
18996                   if (s2<se1) arg2 = compile(++s2,se1,depth1,0,is_single);
18997                 }
18998                 _cimg_mp_check_type(arg1,2,1,0);
18999                 _cimg_mp_check_type(arg2,3,1,0);
19000                 _cimg_mp_check_list(true);
19001                 CImg<ulongT>::vector((ulongT)mp_image_sort,_cimg_mp_slot_nan,p1,arg1,arg2).move_to(code);
19002                 _cimg_mp_return_nan();
19003               }
19004             }
19005 
19006             if (!std::strncmp(ss,"sqr(",4)) { // Square
19007               _cimg_mp_op("Function 'sqr()'");
19008               arg1 = compile(ss4,se1,depth1,0,is_single);
19009               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1);
19010               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1]));
19011               _cimg_mp_scalar1(mp_sqr,arg1);
19012             }
19013 
19014             if (!std::strncmp(ss,"sqrt(",5)) { // Square root
19015               _cimg_mp_op("Function 'sqrt()'");
19016               arg1 = compile(ss5,se1,depth1,0,is_single);
19017               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1);
19018               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1]));
19019               _cimg_mp_scalar1(mp_sqrt,arg1);
19020             }
19021 
19022             if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed
19023               _cimg_mp_op("Function 'srand()'");
19024               arg1 = ss6<se1?compile(ss6,se1,depth1,0,is_single):~0U;
19025               if (arg1!=~0U) { _cimg_mp_check_type(arg1,1,1,0); _cimg_mp_scalar1(mp_srand,arg1); }
19026               _cimg_mp_scalar0(mp_srand0);
19027             }
19028 
19029             if (!std::strncmp(ss,"stats(",6)) { // Image statistics
19030               _cimg_mp_op("Function 'stats()'");
19031               if (*ss6=='#') { // Index specified
19032                 p1 = compile(ss7,se1,depth1,0,is_single);
19033                 _cimg_mp_check_list(false);
19034               } else { if (ss6!=se1) break; p1 = ~0U; }
19035               pos = vector(14);
19036               CImg<ulongT>::vector((ulongT)mp_image_stats,pos,p1).move_to(code);
19037               _cimg_mp_return(pos);
19038             }
19039 
19040             if (!std::strncmp(ss,"stov(",5)) { // String to double
19041               _cimg_mp_op("Function 'stov()'");
19042               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19043               arg1 = compile(ss5,s1,depth1,0,is_single);
19044               arg2 = arg3 = 0;
19045               if (s1<se1) {
19046                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19047                 arg2 = compile(++s1,s2,depth1,0,is_single);
19048                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):0;
19049               }
19050               _cimg_mp_check_type(arg2,2,1,0);
19051               _cimg_mp_check_type(arg3,3,1,0);
19052               p1 = _cimg_mp_size(arg1);
19053               _cimg_mp_scalar4(mp_stov,arg1,p1,arg2,arg3);
19054             }
19055 
19056             if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD
19057               _cimg_mp_op("Function 'svd()'");
19058               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19059               arg1 = compile(ss4,s1,depth1,0,is_single);
19060               arg2 = s1<se1?compile(++s1,se1,depth1,0,is_single):1;
19061               _cimg_mp_check_type(arg1,1,2,0);
19062               _cimg_mp_check_constant(arg2,2,3);
19063               p1 = _cimg_mp_size(arg1);
19064               p2 = (unsigned int)mem[arg2];
19065               p3 = p1/p2;
19066               if (p3*p2!=p1) {
19067                 *se = saved_char;
19068                 s0 = ss - 4>expr._data?ss - 4:expr._data;
19069                 cimg::strellipsize(s0,64);
19070                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19071                                             "CImg<%s>::%s: %s: Type of first argument ('%s') "
19072                                             "does not match with second argument 'nb_colsA=%u', "
19073                                             "in expression '%s%s%s'.",
19074                                             pixel_type(),_cimg_mp_calling_function,s_op,
19075                                             s_type(arg1)._data,p2,
19076                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19077               }
19078               pos = vector(p1 + p2 + p2*p2);
19079               CImg<ulongT>::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code);
19080               _cimg_mp_return(pos);
19081             }
19082             break;
19083 
19084           case 't' :
19085             if (!std::strncmp(ss,"tan(",4)) { // Tangent
19086               _cimg_mp_op("Function 'tan()'");
19087               arg1 = compile(ss4,se1,depth1,0,is_single);
19088               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1);
19089               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1]));
19090               _cimg_mp_scalar1(mp_tan,arg1);
19091             }
19092 
19093             if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent
19094               _cimg_mp_op("Function 'tanh()'");
19095               arg1 = compile(ss5,se1,depth1,0,is_single);
19096               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1);
19097               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1]));
19098               _cimg_mp_scalar1(mp_tanh,arg1);
19099             }
19100 
19101             if (!std::strncmp(ss,"trace(",6)) { // Matrix trace
19102               _cimg_mp_op("Function 'trace()'");
19103               arg1 = compile(ss6,se1,depth1,0,is_single);
19104               _cimg_mp_check_matrix_square(arg1,1);
19105               p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
19106               _cimg_mp_scalar2(mp_trace,arg1,p1);
19107             }
19108 
19109             if (!std::strncmp(ss,"transp(",7)) { // Matrix transpose
19110               _cimg_mp_op("Function 'transp()'");
19111               s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19112               arg1 = compile(ss7,s1,depth1,0,is_single);
19113               arg2 = compile(++s1,se1,depth1,0,is_single);
19114               _cimg_mp_check_type(arg1,1,2,0);
19115               _cimg_mp_check_constant(arg2,2,3);
19116               p1 = _cimg_mp_size(arg1);
19117               p2 = (unsigned int)mem[arg2];
19118               p3 = p1/p2;
19119               if (p2*p3!=p1) {
19120                 *se = saved_char;
19121                 s0 = ss - 4>expr._data?ss - 4:expr._data;
19122                 cimg::strellipsize(s0,64);
19123                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19124                                             "CImg<%s>::%s: %s: Size of first argument ('%s') does not match "
19125                                             "second argument 'nb_cols=%u', in expression '%s%s%s'.",
19126                                             pixel_type(),_cimg_mp_calling_function,s_op,
19127                                             s_type(arg1)._data,p2,
19128                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19129               }
19130               pos = vector(p3*p2);
19131               CImg<ulongT>::vector((ulongT)mp_transp,pos,arg1,p2,p3).move_to(code);
19132               _cimg_mp_return(pos);
19133             }
19134             break;
19135 
19136           case 'u' :
19137             if (*ss1=='(') { // Random value with uniform distribution
19138               _cimg_mp_op("Function 'u()'");
19139               if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1);
19140               s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19141               arg1 = compile(ss2,s1,depth1,0,is_single);
19142               if (s1<se1) arg2 = compile(++s1,se1,depth1,0,is_single); else { arg2 = arg1; arg1 = 0; }
19143               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
19144               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_u,arg1,arg2);
19145               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_u,arg1,arg2);
19146               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_u,arg1,arg2);
19147               _cimg_mp_scalar2(mp_u,arg1,arg2);
19148             }
19149 
19150             if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable
19151               _cimg_mp_op("Function 'unref()'");
19152               arg1 = ~0U;
19153               for (s0 = ss6; s0<se1; s0 = s1) {
19154                 if (s0>ss6 && *s0==',') ++s0;
19155                 s1 = s0; while (s1<se1 && *s1!=',') ++s1;
19156                 c1 = *s1;
19157                 if (s1>s0) {
19158                   *s1 = 0;
19159                   arg2 = arg3 = ~0U;
19160                   if (s0[0]=='w' && s0[1]=='h' && !s0[2]) arg1 = reserved_label[arg3 = 0];
19161                   else if (s0[0]=='w' && s0[1]=='h' && s0[2]=='d' && !s0[3]) arg1 = reserved_label[arg3 = 1];
19162                   else if (s0[0]=='w' && s0[1]=='h' && s0[2]=='d' && s0[3]=='s' && !s0[4])
19163                     arg1 = reserved_label[arg3 = 2];
19164                   else if (s0[0]=='p' && s0[1]=='i' && !s0[2]) arg1 = reserved_label[arg3 = 3];
19165                   else if (s0[0]=='i' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 4];
19166                   else if (s0[0]=='i' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 5];
19167                   else if (s0[0]=='i' && s0[1]=='a' && !s0[2]) arg1 = reserved_label[arg3 = 6];
19168                   else if (s0[0]=='i' && s0[1]=='v' && !s0[2]) arg1 = reserved_label[arg3 = 7];
19169                   else if (s0[0]=='i' && s0[1]=='s' && !s0[2]) arg1 = reserved_label[arg3 = 8];
19170                   else if (s0[0]=='i' && s0[1]=='p' && !s0[2]) arg1 = reserved_label[arg3 = 9];
19171                   else if (s0[0]=='i' && s0[1]=='c' && !s0[2]) arg1 = reserved_label[arg3 = 10];
19172                   else if (s0[0]=='x' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 11];
19173                   else if (s0[0]=='y' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 12];
19174                   else if (s0[0]=='z' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 13];
19175                   else if (s0[0]=='c' && s0[1]=='m' && !s0[2]) arg1 = reserved_label[arg3 = 14];
19176                   else if (s0[0]=='x' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 15];
19177                   else if (s0[0]=='y' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 16];
19178                   else if (s0[0]=='z' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 17];
19179                   else if (s0[0]=='c' && s0[1]=='M' && !s0[2]) arg1 = reserved_label[arg3 = 18];
19180                   else if (s0[0]=='i' && s0[1]>='0' && s0[1]<='9' && !s0[2])
19181                     arg1 = reserved_label[arg3 = 19 + s0[1] - '0'];
19182                   else if (!std::strcmp(s0,"interpolation")) arg1 = reserved_label[arg3 = 29];
19183                   else if (!std::strcmp(s0,"boundary")) arg1 = reserved_label[arg3 = 30];
19184                   else if (s0[1]) { // Multi-char variable
19185                     cimglist_for(variable_def,i) if (!std::strcmp(s0,variable_def[i])) {
19186                       arg1 = variable_pos[i]; arg2 = i; break;
19187                     }
19188                   } else arg1 = reserved_label[arg3 = *s0]; // Single-char variable
19189 
19190                   if (arg1!=~0U) {
19191                     if (arg2==~0U) { if (arg3!=~0U) reserved_label[arg3] = ~0U; }
19192                     else {
19193                       variable_def.remove(arg2);
19194                       if (arg2<variable_pos._width - 1)
19195                         std::memmove(variable_pos._data + arg2,variable_pos._data + arg2 + 1,
19196                                      sizeof(uintT)*(variable_pos._width - arg2 - 1));
19197                       --variable_pos._width;
19198                     }
19199                   }
19200                   *s1 = c1;
19201                 } else compile(s0,s1,depth1,0,is_single); // Will throw a 'missing argument' exception
19202               }
19203               _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable.
19204             }
19205 
19206             if (!std::strncmp(ss,"uppercase(",10)) { // Upper case
19207               _cimg_mp_op("Function 'uppercase()'");
19208               arg1 = compile(ss + 10,se1,depth1,0,is_single);
19209               if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1);
19210               if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::uppercase(mem[arg1]));
19211               _cimg_mp_scalar1(mp_uppercase,arg1);
19212             }
19213             break;
19214 
19215           case 'v' :
19216             if ((cimg_sscanf(ss,"vector%u%c",&(arg1=~0U),&sep)==2 && sep=='(' && arg1>0) ||
19217                 !std::strncmp(ss,"vector(",7) ||
19218                 (!std::strncmp(ss,"vector",6) && ss7<se1 && (s=std::strchr(ss7,'('))!=0)) { // Vector
19219               _cimg_mp_op("Function 'vector()'");
19220               arg2 = 0; // Number of specified values.
19221               if (arg1==~0U && *ss6!='(') {
19222                 arg1 = compile(ss6,s++,depth1,0,is_single);
19223                 _cimg_mp_check_constant(arg1,0,3);
19224                 arg1 = (unsigned int)mem[arg1];
19225               } else s = std::strchr(ss6,'(') + 1;
19226 
19227               if (s<se1 || arg1==~0U) for ( ; s<se; ++s) {
19228                   ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19229                                  (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19230                   arg3 = compile(s,ns,depth1,0,is_single);
19231                   if (_cimg_mp_is_vector(arg3)) {
19232                     arg4 = _cimg_mp_size(arg3);
19233                     CImg<ulongT>::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode);
19234                     arg2+=arg4;
19235                   } else { CImg<ulongT>::vector(arg3).move_to(l_opcode); ++arg2; }
19236                   s = ns;
19237                 }
19238               if (arg1==~0U) arg1 = arg2;
19239               _cimg_mp_check_vector0(arg1);
19240               pos = vector(arg1);
19241               l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
19242               (l_opcode>'y').move_to(opcode);
19243               opcode[2] = opcode._height;
19244               opcode.move_to(code);
19245               _cimg_mp_return(pos);
19246             }
19247 
19248             if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string
19249               _cimg_mp_op("Function 'vtos()'");
19250               s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19251               arg1 = compile(ss5,s1,depth1,0,is_single);
19252               arg2 = 0; arg3 = ~0U;
19253               if (s1<se1) {
19254                 s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
19255                 arg2 = compile(++s1,s2,depth1,0,is_single);
19256                 arg3 = s2<se1?compile(++s2,se1,depth1,0,is_single):~0U;
19257               }
19258               _cimg_mp_check_type(arg2,2,1,0);
19259               if (arg3==~0U) { // Auto-guess best output vector size
19260                 p1 = _cimg_mp_size(arg1);
19261                 p1 = p1?19*p1 - 1:18;
19262               } else {
19263                 _cimg_mp_check_constant(arg3,3,3);
19264                 p1 = (unsigned int)mem[arg3];
19265               }
19266               pos = vector(p1);
19267               CImg<ulongT>::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code);
19268               _cimg_mp_return(pos);
19269             }
19270             break;
19271 
19272           case 'w' :
19273             if (*ss1=='(') { // Image width
19274               _cimg_mp_op("Function 'w()'");
19275               if (*ss2=='#') { // Index specified
19276                 p1 = compile(ss3,se1,depth1,0,is_single);
19277                 _cimg_mp_check_list(false);
19278               } else { if (ss2!=se1) break; p1 = ~0U; }
19279               pos = scalar();
19280               CImg<ulongT>::vector((ulongT)mp_image_w,pos,p1).move_to(code);
19281               _cimg_mp_return(pos);
19282             }
19283 
19284             if (*ss1=='h' && *ss2=='(') { // Image width*height
19285               _cimg_mp_op("Function 'wh()'");
19286               if (*ss3=='#') { // Index specified
19287                 p1 = compile(ss4,se1,depth1,0,is_single);
19288                 _cimg_mp_check_list(false);
19289               } else { if (ss3!=se1) break; p1 = ~0U; }
19290               pos = scalar();
19291               CImg<ulongT>::vector((ulongT)mp_image_wh,pos,p1).move_to(code);
19292               _cimg_mp_return(pos);
19293             }
19294 
19295             if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth
19296               _cimg_mp_op("Function 'whd()'");
19297               if (*ss4=='#') { // Index specified
19298                 p1 = compile(ss5,se1,depth1,0,is_single);
19299                 _cimg_mp_check_list(false);
19300               } else { if (ss4!=se1) break; p1 = ~0U; }
19301               pos = scalar();
19302               CImg<ulongT>::vector((ulongT)mp_image_whd,pos,p1).move_to(code);
19303               _cimg_mp_return(pos);
19304             }
19305 
19306             if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum
19307               _cimg_mp_op("Function 'whds()'");
19308               if (*ss5=='#') { // Index specified
19309                 p1 = compile(ss6,se1,depth1,0,is_single);
19310                 _cimg_mp_check_list(false);
19311               } else { if (ss5!=se1) break; p1 = ~0U; }
19312               pos = scalar();
19313               CImg<ulongT>::vector((ulongT)mp_image_whds,pos,p1).move_to(code);
19314               _cimg_mp_return(pos);
19315             }
19316 
19317             if (!std::strncmp(ss,"while(",6) || !std::strncmp(ss,"whiledo(",8)) { // While...do
19318               _cimg_mp_op("Function 'whiledo()'");
19319               s0 = *ss5=='('?ss6:ss8;
19320               s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19321               p1 = code._width;
19322               arg1 = compile(s0,s1,depth1,0,is_single);
19323               p2 = code._width;
19324               arg6 = mempos;
19325               pos = compile(++s1,se1,depth1,0,is_single);
19326               _cimg_mp_check_type(arg1,1,1,0);
19327               arg2 = _cimg_mp_size(pos);
19328               CImg<ulongT>::vector((ulongT)mp_whiledo,pos,arg1,p2 - p1,code._width - p2,arg2,
19329                                    pos>=arg6 && !_cimg_mp_is_constant(pos),
19330                                    arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1);
19331               _cimg_mp_return(pos);
19332             }
19333             break;
19334 
19335           case 'x' :
19336             if (!std::strncmp(ss,"xor(",4)) { // Xor
19337               _cimg_mp_op("Function 'xor()'");
19338               s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
19339               arg1 = compile(ss4,s1,depth1,0,is_single);
19340               arg2 = compile(++s1,se1,depth1,0,is_single);
19341               _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
19342               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_xor,arg1,arg2);
19343               if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_xor,arg1,arg2);
19344               if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_xor,arg1,arg2);
19345               if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2))
19346                 _cimg_mp_constant((longT)mem[arg1] ^ (longT)mem[arg2]);
19347               _cimg_mp_scalar2(mp_bitwise_xor,arg1,arg2);
19348             }
19349             break;
19350           }
19351 
19352           if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4) ||
19353               !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) ||
19354               !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) ||
19355               !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) ||
19356               !std::strncmp(ss,"prod(",5) ||
19357               !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) ||
19358               !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions
19359             _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'":
19360                                   ss[3]=='k'?"Function 'argkth()'":
19361                                   ss[4]=='i'?"Function 'argmin()'":
19362                                   "Function 'argmax()'"):
19363                         *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"):
19364                         *ss=='k'?"Function 'kth()'":
19365                         *ss=='p'?"Function 'prod()'":
19366                         *ss=='v'?"Function 'var()'":
19367                         ss[1]=='i'?"Function 'min()'":
19368                         ss[1]=='a'?"Function 'max()'":"Function 'med()'");
19369             op = *ss=='a'?(ss[1]=='v'?mp_avg:ss[3]=='k'?mp_argkth:ss[4]=='i'?mp_argmin:mp_argmax):
19370               *ss=='s'?(ss[1]=='u'?mp_sum:mp_std):
19371               *ss=='k'?mp_kth:
19372               *ss=='p'?mp_prod:
19373               *ss=='v'?mp_var:
19374               ss[1]=='i'?mp_min:
19375               ss[1]=='a'?mp_max:
19376               ss[2]=='a'?mp_avg:
19377               mp_median;
19378             is_sth = true; // Tell if all arguments are constant
19379             pos = scalar();
19380             CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode);
19381             for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
19382               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19383                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19384               arg2 = compile(s,ns,depth1,0,is_single);
19385               if (_cimg_mp_is_vector(arg2))
19386                 CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
19387                                        arg2 + (ulongT)_cimg_mp_size(arg2)).
19388                   move_to(l_opcode);
19389               else CImg<ulongT>::vector(arg2).move_to(l_opcode);
19390               is_sth&=_cimg_mp_is_constant(arg2);
19391               s = ns;
19392             }
19393             (l_opcode>'y').move_to(opcode);
19394             opcode[2] = opcode._height;
19395             if (is_sth) _cimg_mp_constant(op(*this));
19396             opcode.move_to(code);
19397             _cimg_mp_return(pos);
19398           }
19399 
19400           // No corresponding built-in function -> Look for a user-defined macro call.
19401           s0 = strchr(ss,'(');
19402           if (s0) {
19403             variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
19404 
19405             // Count number of specified arguments.
19406             p1 = 0;
19407             for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) {
19408               while (*s && (signed char)*s<=' ') ++s;
19409               if (*s==')' && !p1) break;
19410               ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19411                              (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19412             }
19413 
19414             arg3 = 0; // Number of possible name matches
19415             cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 &&
19416                                           macro_def[l].back()==(char)p1) {
19417               p2 = (unsigned int)macro_def[l].back(); // Number of required arguments
19418               CImg<charT> _expr = macro_body[l]; // Expression to be substituted
19419 
19420               p1 = 1; // Indice of current parsed argument
19421               for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments
19422                 while (*s && (signed char)*s<=' ') ++s;
19423                 if (*s==')' && p1==1) break; // Function has no arguments
19424                 if (p1>p2) { ++p1; break; }
19425                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19426                                (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
19427                 variable_name.assign(s,(unsigned int)(ns - s + 1)).back() = 0; // Argument to write
19428                 arg2 = 0;
19429                 cimg_forX(_expr,k) {
19430                   if (_expr[k]==(char)p1) { // Perform argument substitution
19431                     arg1 = _expr._width;
19432                     _expr.resize(arg1 + variable_name._width - 2,1,1,1,0);
19433                     std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1);
19434                     std::memcpy(_expr._data + k,variable_name,variable_name._width - 1);
19435                     k+=variable_name._width - 2;
19436                   }
19437                   ++arg2;
19438                 }
19439               }
19440 
19441               // Recompute 'pexpr' and 'level' for evaluating substituted expression.
19442               CImg<charT> _pexpr(_expr._width);
19443               ns = _pexpr._data;
19444               for (ps = _expr._data, c1 = ' '; *ps; ++ps) {
19445                 if ((signed char)*ps>' ') c1 = *ps;
19446                 *(ns++) = c1;
19447               }
19448               *ns = 0;
19449 
19450               CImg<uintT> _level = get_level(_expr);
19451               expr.swap(_expr);
19452               pexpr.swap(_pexpr);
19453               level.swap(_level);
19454               s0 = user_macro;
19455               user_macro = macro_def[l];
19456               pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_single);
19457               user_macro = s0;
19458               level.swap(_level);
19459               pexpr.swap(_pexpr);
19460               expr.swap(_expr);
19461               _cimg_mp_return(pos);
19462             }
19463 
19464             if (arg3) { // Macro name matched but number of arguments does not
19465               CImg<uintT> sig_nargs(arg3);
19466               arg1 = 0;
19467               cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name))
19468                 sig_nargs[arg1++] = (unsigned int)macro_def[l].back();
19469               *se = saved_char;
19470               cimg::strellipsize(variable_name,64);
19471               s0 = ss - 4>expr._data?ss - 4:expr._data;
19472               cimg::strellipsize(s0,64);
19473               if (sig_nargs._width>1) {
19474                 sig_nargs.sort();
19475                 arg1 = sig_nargs.back();
19476                 --sig_nargs._width;
19477                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19478                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
19479                                             "does not match macro declaration (defined for %s or %u arguments), "
19480                                             "in expression '%s%s%s'.",
19481                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
19482                                             p1,sig_nargs.value_string()._data,arg1,
19483                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19484               } else
19485                 throw CImgArgumentException("[" cimg_appname "_math_parser] "
19486                                             "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
19487                                             "does not match macro declaration (defined for %u argument%s), "
19488                                             "in expression '%s%s%s'.",
19489                                             pixel_type(),_cimg_mp_calling_function,variable_name._data,
19490                                             p1,*sig_nargs,*sig_nargs!=1?"s":"",
19491                                             s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19492             }
19493           }
19494         } // if (se1==')')
19495 
19496         // Char / string initializer.
19497         if (*se1=='\'' &&
19498             ((se1>ss && *ss=='\'') ||
19499             (se1>ss1 && *ss=='_' && *ss1=='\''))) {
19500           if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; }
19501           else { _cimg_mp_op("String initializer"); s1 = ss1; }
19502           arg1 = (unsigned int)(se1 - s1); // Original string length.
19503           if (arg1) {
19504             CImg<charT>(s1,arg1 + 1).move_to(variable_name).back() = 0;
19505             cimg::strunescape(variable_name);
19506             arg1 = (unsigned int)std::strlen(variable_name);
19507           }
19508           if (!arg1) _cimg_mp_return(0); // Empty string -> 0
19509           if (*ss=='_') {
19510             if (arg1==1) _cimg_mp_constant(*variable_name);
19511             *se = saved_char;
19512             cimg::strellipsize(variable_name,64);
19513             s0 = ss - 4>expr._data?ss - 4:expr._data;
19514             cimg::strellipsize(s0,64);
19515             throw CImgArgumentException("[" cimg_appname "_math_parser] "
19516                                         "CImg<%s>::%s: %s: Literal %s contains more than one character, "
19517                                         "in expression '%s%s%s'.",
19518                                         pixel_type(),_cimg_mp_calling_function,s_op,
19519                                         ss1,
19520                                         s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19521           }
19522           pos = vector(arg1);
19523           CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
19524           CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
19525           std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
19526           (l_opcode>'y').move_to(code);
19527           _cimg_mp_return(pos);
19528         }
19529 
19530         // Vector initializer [ ... ].
19531         if (*ss=='[' && *se1==']') {
19532           _cimg_mp_op("Vector initializer");
19533           s1 = ss1; while (s1<se2 && (signed char)*s1<=' ') ++s1;
19534           s2 = se2; while (s2>s1 && (signed char)*s2<=' ') --s2;
19535           if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string
19536             arg1 = (unsigned int)(s2 - s1 - 1); // Original string length.
19537             if (arg1) {
19538               CImg<charT>(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0;
19539               cimg::strunescape(variable_name);
19540               arg1 = (unsigned int)std::strlen(variable_name);
19541             }
19542             if (!arg1) _cimg_mp_return(0); // Empty string -> 0
19543             pos = vector(arg1);
19544             CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
19545             CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
19546             std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
19547             (l_opcode>'y').move_to(code);
19548           } else { // Vector values provided as list of items
19549             arg1 = 0; // Number of specified values.
19550             if (*ss1!=']') for (s = ss1; s<se; ++s) {
19551                 ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
19552                                (*ns!=']' || level[ns - expr._data]!=clevel)) ++ns;
19553                 arg2 = compile(s,ns,depth1,0,is_single);
19554                 if (_cimg_mp_is_vector(arg2)) {
19555                   arg3 = _cimg_mp_size(arg2);
19556                   CImg<ulongT>::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode);
19557                   arg1+=arg3;
19558                 } else { CImg<ulongT>::vector(arg2).move_to(l_opcode); ++arg1; }
19559                 s = ns;
19560               }
19561             _cimg_mp_check_vector0(arg1);
19562             pos = vector(arg1);
19563             l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
19564             (l_opcode>'y').move_to(opcode);
19565             opcode[2] = opcode._height;
19566             opcode.move_to(code);
19567           }
19568           _cimg_mp_return(pos);
19569         }
19570 
19571         // Variables related to the input list of images.
19572         if (*ss1=='#' && ss2<se) {
19573           arg1 = compile(ss2,se,depth1,0,is_single);
19574           p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
19575           switch (*ss) {
19576           case 'w' : // w#ind
19577             if (!listin) _cimg_mp_return(0);
19578             if (p1!=~0U) _cimg_mp_constant(listin[p1]._width);
19579             _cimg_mp_scalar1(mp_list_width,arg1);
19580           case 'h' : // h#ind
19581             if (!listin) _cimg_mp_return(0);
19582             if (p1!=~0U) _cimg_mp_constant(listin[p1]._height);
19583             _cimg_mp_scalar1(mp_list_height,arg1);
19584           case 'd' : // d#ind
19585             if (!listin) _cimg_mp_return(0);
19586             if (p1!=~0U) _cimg_mp_constant(listin[p1]._depth);
19587             _cimg_mp_scalar1(mp_list_depth,arg1);
19588           case 'r' : // r#ind
19589             if (!listin) _cimg_mp_return(0);
19590             if (p1!=~0U) _cimg_mp_constant(listin[p1]._is_shared);
19591             _cimg_mp_scalar1(mp_list_is_shared,arg1);
19592           case 's' : // s#ind
19593             if (!listin) _cimg_mp_return(0);
19594             if (p1!=~0U) _cimg_mp_constant(listin[p1]._spectrum);
19595             _cimg_mp_scalar1(mp_list_spectrum,arg1);
19596           case 'i' : // i#ind
19597             if (!listin) _cimg_mp_return(0);
19598             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,_cimg_mp_slot_c,
19599                              0,_cimg_mp_boundary);
19600           case 'I' : // I#ind
19601             p2 = p1!=~0U?listin[p1]._spectrum:listin._width?~0U:0;
19602             _cimg_mp_check_vector0(p2);
19603             pos = vector(p2);
19604             CImg<ulongT>::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code);
19605             _cimg_mp_return(pos);
19606           case 'R' : // R#ind
19607             if (!listin) _cimg_mp_return(0);
19608             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,
19609                              0,_cimg_mp_boundary);
19610           case 'G' : // G#ind
19611             if (!listin) _cimg_mp_return(0);
19612             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,
19613                              0,_cimg_mp_boundary);
19614           case 'B' : // B#ind
19615             if (!listin) _cimg_mp_return(0);
19616             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,
19617                              0,_cimg_mp_boundary);
19618           case 'A' : // A#ind
19619             if (!listin) _cimg_mp_return(0);
19620             _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,
19621                              0,_cimg_mp_boundary);
19622           }
19623         }
19624 
19625         if (*ss1 && *ss2=='#' && ss3<se) {
19626           arg1 = compile(ss3,se,depth1,0,is_single);
19627           p1 = (unsigned int)(listin._width && _cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
19628           if (*ss=='w' && *ss1=='h') { // wh#ind
19629             if (!listin) _cimg_mp_return(0);
19630             if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height);
19631             _cimg_mp_scalar1(mp_list_wh,arg1);
19632           }
19633           arg2 = ~0U;
19634 
19635           if (*ss=='i') {
19636             if (*ss1=='c') { // ic#ind
19637               if (!listin) _cimg_mp_return(0);
19638               if (_cimg_mp_is_constant(arg1)) {
19639                 if (!list_median) list_median.assign(listin._width);
19640                 if (!list_median[p1]) CImg<doubleT>::vector(listin[p1].median()).move_to(list_median[p1]);
19641                 _cimg_mp_constant(*list_median[p1]);
19642               }
19643               _cimg_mp_scalar1(mp_list_median,arg1);
19644             }
19645             if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind
19646               if (!listin) _cimg_mp_return(0);
19647               _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0',
19648                                0,_cimg_mp_boundary);
19649             }
19650             switch (*ss1) {
19651             case 'm' : arg2 = 0; break; // im#ind
19652             case 'M' : arg2 = 1; break; // iM#ind
19653             case 'a' : arg2 = 2; break; // ia#ind
19654             case 'v' : arg2 = 3; break; // iv#ind
19655             case 's' : arg2 = 12; break; // is#ind
19656             case 'p' : arg2 = 13; break; // ip#ind
19657             }
19658           } else if (*ss1=='m') switch (*ss) {
19659             case 'x' : arg2 = 4; break; // xm#ind
19660             case 'y' : arg2 = 5; break; // ym#ind
19661             case 'z' : arg2 = 6; break; // zm#ind
19662             case 'c' : arg2 = 7; break; // cm#ind
19663             } else if (*ss1=='M') switch (*ss) {
19664             case 'x' : arg2 = 8; break; // xM#ind
19665             case 'y' : arg2 = 9; break; // yM#ind
19666             case 'z' : arg2 = 10; break; // zM#ind
19667             case 'c' : arg2 = 11; break; // cM#ind
19668             }
19669           if (arg2!=~0U) {
19670             if (!listin) _cimg_mp_return(0);
19671             if (_cimg_mp_is_constant(arg1)) {
19672               if (!list_stats) list_stats.assign(listin._width);
19673               if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false);
19674               _cimg_mp_constant(list_stats(p1,arg2));
19675             }
19676             _cimg_mp_scalar2(mp_list_stats,arg1,arg2);
19677           }
19678         }
19679 
19680         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
19681           arg1 = compile(ss4,se,depth1,0,is_single);
19682           if (!listin) _cimg_mp_return(0);
19683           p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
19684           if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth);
19685           _cimg_mp_scalar1(mp_list_whd,arg1);
19686         }
19687         if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
19688           arg1 = compile(ss5,se,depth1,0,is_single);
19689           if (!listin) _cimg_mp_return(0);
19690           p1 = (unsigned int)(_cimg_mp_is_constant(arg1)?cimg::mod((int)mem[arg1],listin.width()):~0U);
19691           if (p1!=~0U) _cimg_mp_constant(listin[p1]._width*listin[p1]._height*listin[p1]._depth*listin[p1]._spectrum);
19692           _cimg_mp_scalar1(mp_list_whds,arg1);
19693         }
19694 
19695         if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(_cimg_mp_interpolation); // interpolation
19696         if (!std::strcmp(ss,"boundary")) _cimg_mp_return(_cimg_mp_boundary); // boundary
19697 
19698         // No known item found, assuming this is an already initialized variable.
19699         variable_name.assign(ss,(unsigned int)(se - ss + 1)).back() = 0;
19700         if (variable_name[1]) { // Multi-char variable
19701           cimglist_for(variable_def,i) if (!std::strcmp(variable_name,variable_def[i]))
19702             _cimg_mp_return(variable_pos[i]);
19703         } else if (reserved_label[*variable_name]!=~0U) // Single-char variable
19704           _cimg_mp_return(reserved_label[*variable_name]);
19705 
19706         // Reached an unknown item -> error.
19707         is_sth = true; // is_valid_variable_name
19708         if (*variable_name>='0' && *variable_name<='9') is_sth = false;
19709         else for (ns = variable_name._data; *ns; ++ns)
19710                if (!is_varchar(*ns)) { is_sth = false; break; }
19711 
19712         *se = saved_char;
19713         c1 = *se1;
19714         cimg::strellipsize(variable_name,64);
19715         s0 = ss - 4>expr._data?ss - 4:expr._data;
19716         cimg::strellipsize(s0,64);
19717         if (is_sth)
19718           throw CImgArgumentException("[" cimg_appname "_math_parser] "
19719                                       "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.",
19720                                       pixel_type(),_cimg_mp_calling_function,
19721                                       variable_name._data,
19722                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19723         s1 = std::strchr(ss,'(');
19724         s_op = s1 && c1==')'?"function call":"item";
19725         throw CImgArgumentException("[" cimg_appname "_math_parser] "
19726                                     "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.",
19727                                     pixel_type(),_cimg_mp_calling_function,
19728                                     s_op,variable_name._data,
19729                                     s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
19730       }
19731 
19732       // Evaluation procedure.
19733       double operator()(const double x, const double y, const double z, const double c) {
19734         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
19735         for (p_code = code; p_code<p_code_end; ++p_code) {
19736           opcode._data = p_code->_data;
19737           const ulongT target = opcode[1];
19738           mem[target] = _cimg_mp_defunc(*this);
19739         }
19740         return *result;
19741       }
19742 
19743       // Evaluation procedure (return output values in vector 'output').
19744       template<typename t>
19745       void operator()(const double x, const double y, const double z, const double c, t *const output) {
19746         mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
19747         for (p_code = code; p_code<p_code_end; ++p_code) {
19748           opcode._data = p_code->_data;
19749           const ulongT target = opcode[1];
19750           mem[target] = _cimg_mp_defunc(*this);
19751         }
19752         if (result_dim) {
19753           const double *ptrs = result + 1;
19754           t *ptrd = output;
19755           for (unsigned int k = 0; k<result_dim; ++k) *(ptrd++) = (t)*(ptrs++);
19756         } else *output = (t)*result;
19757       }
19758 
19759       // Evaluation procedure for the end() blocks.
19760       void end() {
19761         if (code_end.is_empty()) return;
19762         if (imgin) {
19763           mem[_cimg_mp_slot_x] = imgin._width - 1.0;
19764           mem[_cimg_mp_slot_y] = imgin._height - 1.0f;
19765           mem[_cimg_mp_slot_z] = imgin._depth - 1.0f;
19766           mem[_cimg_mp_slot_c] = imgin._spectrum - 1.0f;
19767         } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
19768         p_code_end = code_end.end();
19769         for (p_code = code_end; p_code<p_code_end; ++p_code) {
19770           opcode._data = p_code->_data;
19771           const ulongT target = opcode[1];
19772           mem[target] = _cimg_mp_defunc(*this);
19773         }
19774       }
19775 
19776       // Return type of a memory element as a string.
19777       CImg<charT> s_type(const unsigned int arg) const {
19778         CImg<charT> res;
19779         if (_cimg_mp_is_vector(arg)) { // Vector
19780           CImg<charT>::string("vectorXXXXXXXXXXXXXXXX").move_to(res);
19781           std::sprintf(res._data + 6,"%u",_cimg_mp_size(arg));
19782         } else CImg<charT>::string("scalar").move_to(res);
19783         return res;
19784       }
19785 
19786       // Insert constant value in memory.
19787       unsigned int constant(const double val) {
19788 
19789         // Search for built-in constant.
19790         if (cimg::type<double>::is_nan(val)) return _cimg_mp_slot_nan;
19791         if (val==(double)(int)val) {
19792           if (val>=0 && val<=10) return (unsigned int)val;
19793           if (val<0 && val>=-5) return (unsigned int)(10 - val);
19794         }
19795         if (val==0.5) return 16;
19796 
19797         // Search for constant already requested before (in const cache).
19798         unsigned int ind = ~0U;
19799         if (constcache_size<1024) {
19800           if (!constcache_size) {
19801             constcache_vals.assign(16,1,1,1,0);
19802             constcache_inds.assign(16,1,1,1,0);
19803             *constcache_vals = val;
19804             constcache_size = 1;
19805             ind = 0;
19806           } else { // Dichotomic search
19807             const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1];
19808             if (val_beg>=val) ind = 0;
19809             else if (val_end==val) ind = constcache_size - 1;
19810             else if (val_end<val) ind = constcache_size;
19811             else {
19812               unsigned int i0 = 1, i1 = constcache_size - 2;
19813               while (i0<=i1) {
19814                 const unsigned int mid = (i0 + i1)/2;
19815                 if (constcache_vals[mid]==val) { i0 = mid; break; }
19816                 else if (constcache_vals[mid]<val) i0 = mid + 1;
19817                 else i1 = mid - 1;
19818               }
19819               ind = i0;
19820             }
19821 
19822             if (ind>=constcache_size || constcache_vals[ind]!=val) {
19823               ++constcache_size;
19824               if (constcache_size>constcache_vals._width) {
19825                 constcache_vals.resize(-200,1,1,1,0);
19826                 constcache_inds.resize(-200,1,1,1,0);
19827               }
19828               const int l = constcache_size - (int)ind - 1;
19829               if (l>0) {
19830                 std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double));
19831                 std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int));
19832               }
19833               constcache_vals[ind] = val;
19834               constcache_inds[ind] = 0;
19835             }
19836           }
19837           if (constcache_inds[ind]) return constcache_inds[ind];
19838         }
19839 
19840         // Insert new constant in memory if necessary.
19841         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); }
19842         const unsigned int pos = mempos++;
19843         mem[pos] = val;
19844         memtype[pos] = 1; // Set constant property
19845         if (ind!=~0U) constcache_inds[ind] = pos;
19846         return pos;
19847       }
19848 
19849       // Insert code instructions for processing scalars.
19850       unsigned int scalar() { // Insert new scalar in memory.
19851         if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); }
19852         return mempos++;
19853       }
19854 
19855       unsigned int scalar0(const mp_func op) {
19856         const unsigned int pos = scalar();
19857         CImg<ulongT>::vector((ulongT)op,pos).move_to(code);
19858         return pos;
19859       }
19860 
19861       unsigned int scalar1(const mp_func op, const unsigned int arg1) {
19862         const unsigned int pos =
19863           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:scalar();
19864         CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code);
19865         return pos;
19866       }
19867 
19868       unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
19869         const unsigned int pos =
19870           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19871           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:scalar();
19872         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2).move_to(code);
19873         return pos;
19874       }
19875 
19876       unsigned int scalar3(const mp_func op,
19877                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
19878         const unsigned int pos =
19879           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19880           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19881           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:scalar();
19882         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code);
19883         return pos;
19884       }
19885 
19886       unsigned int scalar4(const mp_func op,
19887                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
19888                            const unsigned int arg4) {
19889         const unsigned int pos =
19890           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19891           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19892           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
19893           arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:scalar();
19894         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code);
19895         return pos;
19896       }
19897 
19898       unsigned int scalar5(const mp_func op,
19899                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
19900                            const unsigned int arg4, const unsigned int arg5) {
19901         const unsigned int pos =
19902           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19903           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19904           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
19905           arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
19906           arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:scalar();
19907         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
19908         return pos;
19909       }
19910 
19911       unsigned int scalar6(const mp_func op,
19912                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
19913                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
19914         const unsigned int pos =
19915           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19916           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19917           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
19918           arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
19919           arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
19920           arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:scalar();
19921         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
19922         return pos;
19923       }
19924 
19925       unsigned int scalar7(const mp_func op,
19926                            const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
19927                            const unsigned int arg4, const unsigned int arg5, const unsigned int arg6,
19928                            const unsigned int arg7) {
19929         const unsigned int pos =
19930           arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
19931           arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
19932           arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
19933           arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
19934           arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
19935           arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
19936           arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:scalar();
19937         CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code);
19938         return pos;
19939       }
19940 
19941       // Return a string that defines the calling function + the user-defined function scope.
19942       CImg<charT> calling_function_s() const {
19943         CImg<charT> res;
19944         const unsigned int
19945           l1 = calling_function?(unsigned int)std::strlen(calling_function):0U,
19946           l2 = user_macro?(unsigned int)std::strlen(user_macro):0U;
19947         if (l2) {
19948           res.assign(l1 + l2 + 48);
19949           cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro);
19950         } else {
19951           res.assign(l1 + l2 + 4);
19952           cimg_snprintf(res,res._width,"%s()",calling_function);
19953         }
19954         return res;
19955       }
19956 
19957       // Return true if specified argument can be a part of an allowed  variable name.
19958       bool is_varchar(const char c) const {
19959         return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
19960       }
19961 
19962       // Insert code instructions for processing vectors.
19963       bool is_comp_vector(const unsigned int arg) const {
19964         unsigned int siz = _cimg_mp_size(arg);
19965         if (siz>8) return false;
19966         const int *ptr = memtype.data(arg + 1);
19967         bool is_tmp = true;
19968         while (siz-->0) if (*(ptr++)) { is_tmp = false; break; }
19969         return is_tmp;
19970       }
19971 
19972       void set_variable_vector(const unsigned int arg) {
19973         unsigned int siz = _cimg_mp_size(arg);
19974         int *ptr = memtype.data(arg + 1);
19975         while (siz-->0) *(ptr++) = -1;
19976       }
19977 
19978       unsigned int vector(const unsigned int siz) { // Insert new vector of specified size in memory
19979         if (mempos + siz>=mem._width) {
19980           mem.resize(2*mem._width + siz,1,1,1,0);
19981           memtype.resize(mem._width,1,1,1,0);
19982         }
19983         const unsigned int pos = mempos++;
19984         mem[pos] = cimg::type<double>::nan();
19985         memtype[pos] = siz + 1;
19986         mempos+=siz;
19987         return pos;
19988       }
19989 
19990       unsigned int vector(const unsigned int siz, const double value) { // Insert new initialized vector
19991         const unsigned int pos = vector(siz);
19992         double *ptr = &mem[pos] + 1;
19993         for (unsigned int i = 0; i<siz; ++i) *(ptr++) = value;
19994         return pos;
19995       }
19996 
19997       unsigned int vector_copy(const unsigned int arg) { // Insert new copy of specified vector in memory
19998         const unsigned int
19999           siz = _cimg_mp_size(arg),
20000           pos = vector(siz);
20001         CImg<ulongT>::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code);
20002         return pos;
20003       }
20004 
20005       void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) {
20006         const unsigned int siz = _cimg_mp_size(pos);
20007         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code);
20008         else {
20009           code.insert(siz);
20010           for (unsigned int k = 1; k<=siz; ++k)
20011             CImg<ulongT>::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]);
20012         }
20013       }
20014 
20015       void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) {
20016         const unsigned int siz = _cimg_mp_size(pos);
20017         if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code);
20018         else {
20019           code.insert(siz);
20020           for (unsigned int k = 1; k<=siz; ++k)
20021             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
20022         }
20023       }
20024 
20025       unsigned int vector1_v(const mp_func op, const unsigned int arg1) {
20026         const unsigned int
20027           siz = _cimg_mp_size(arg1),
20028           pos = is_comp_vector(arg1)?arg1:vector(siz);
20029         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code);
20030         else {
20031           code.insert(siz);
20032           for (unsigned int k = 1; k<=siz; ++k)
20033             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
20034         }
20035         return pos;
20036       }
20037 
20038       unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
20039         const unsigned int
20040           siz = _cimg_mp_size(arg1),
20041           pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:vector(siz);
20042         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
20043         else {
20044           code.insert(siz);
20045           for (unsigned int k = 1; k<=siz; ++k)
20046             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]);
20047         }
20048         return pos;
20049       }
20050 
20051       unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
20052         const unsigned int
20053           siz = _cimg_mp_size(arg1),
20054           pos = is_comp_vector(arg1)?arg1:vector(siz);
20055         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
20056         else {
20057           code.insert(siz);
20058           for (unsigned int k = 1; k<=siz; ++k)
20059             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]);
20060         }
20061         return pos;
20062       }
20063 
20064       unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
20065         const unsigned int
20066           siz = _cimg_mp_size(arg2),
20067           pos = is_comp_vector(arg2)?arg2:vector(siz);
20068         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code);
20069         else {
20070           code.insert(siz);
20071           for (unsigned int k = 1; k<=siz; ++k)
20072             CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]);
20073         }
20074         return pos;
20075       }
20076 
20077       unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
20078                                const unsigned int arg3) {
20079         const unsigned int
20080           siz = _cimg_mp_size(arg1),
20081           pos = is_comp_vector(arg1)?arg1:vector(siz);
20082         if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code);
20083         else {
20084           code.insert(siz);
20085           for (unsigned int k = 1; k<=siz; ++k)
20086             CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]);
20087         }
20088         return pos;
20089       }
20090 
20091       // Check if a memory slot is a positive integer constant scalar value.
20092       // 'mode' can be:
20093       // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant }
20094       void check_constant(const unsigned int arg, const unsigned int n_arg,
20095                           const unsigned int mode,
20096                           char *const ss, char *const se, const char saved_char) {
20097         _cimg_mp_check_type(arg,n_arg,1,0);
20098         if (!(_cimg_mp_is_constant(arg) &&
20099               (!mode || (double)(int)mem[arg]==mem[arg]) &&
20100               (mode<2 || mem[arg]>=(mode==3)))) {
20101           const char *s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":
20102             n_arg==4?"Fourth ":n_arg==5?"Fifth ":n_arg==6?"Sixth ":n_arg==7?"Seventh ":n_arg==8?"Eighth ":
20103             n_arg==9?"Ninth ":"One of the ";
20104           *se = saved_char;
20105           char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
20106           cimg::strellipsize(s0,64);
20107           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20108                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, "
20109                                       "in expression '%s%s%s'.",
20110                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20111                                       s_arg,*s_arg?"argument":"Argument",s_type(arg)._data,
20112                                       !mode?"":mode==1?"n integer":
20113                                       mode==2?" positive integer":" strictly positive integer",
20114                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20115         }
20116       }
20117 
20118       // Check a matrix is square.
20119       void check_matrix_square(const unsigned int arg, const unsigned int n_arg,
20120                                char *const ss, char *const se, const char saved_char) {
20121         _cimg_mp_check_type(arg,n_arg,2,0);
20122         const unsigned int
20123           siz = _cimg_mp_size(arg),
20124           n = (unsigned int)cimg::round(std::sqrt((float)siz));
20125         if (n*n!=siz) {
20126           const char *s_arg;
20127           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand ";
20128           else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":"One ";
20129           *se = saved_char;
20130           char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
20131           cimg::strellipsize(s0,64);
20132           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20133                                       "CImg<%s>::%s: %s%s %s%s (of type '%s') "
20134                                       "cannot be considered as a square matrix, in expression '%s%s%s'.",
20135                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20136                                       s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"),
20137                                       s_type(arg)._data,
20138                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20139         }
20140       }
20141 
20142       // Check type compatibility for one argument.
20143       // Bits of 'mode' tells what types are allowed:
20144       // { 1 = scalar | 2 = vectorN }.
20145       // If 'N' is not zero, it also restricts the vectors to be of size N only.
20146       void check_type(const unsigned int arg, const unsigned int n_arg,
20147                       const unsigned int mode, const unsigned int N,
20148                       char *const ss, char *const se, const char saved_char) {
20149         const bool
20150           is_scalar = _cimg_mp_is_scalar(arg),
20151           is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N);
20152         bool cond = false;
20153         if (mode&1) cond|=is_scalar;
20154         if (mode&2) cond|=is_vector;
20155         if (!cond) {
20156           const char *s_arg;
20157           if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand ":"Right-hand ";
20158           else s_arg = !n_arg?"":n_arg==1?"First ":n_arg==2?"Second ":n_arg==3?"Third ":
20159                  n_arg==4?"Fourth ":n_arg==5?"Fifth ":n_arg==6?"Sixth ":n_arg==7?"Seventh ":n_arg==8?"Eighth":
20160                  n_arg==9?"Ninth":"One of the ";
20161           CImg<charT> sb_type(32);
20162           if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'");
20163           else if (mode==2) {
20164             if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N);
20165             else cimg_snprintf(sb_type,sb_type._width,"'vector'");
20166           } else {
20167             if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N);
20168             else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'");
20169           }
20170           *se = saved_char;
20171           char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
20172           cimg::strellipsize(s0,64);
20173           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20174                                       "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), "
20175                                       "in expression '%s%s%s'.",
20176                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20177                                       s_arg,*s_op=='F'?(*s_arg?"argument":"Argument"):(*s_arg?"operand":"Operand"),
20178                                       s_type(arg)._data,sb_type._data,
20179                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20180         }
20181       }
20182 
20183       // Check that listin or listout are not empty.
20184       void check_list(const bool is_out,
20185                       char *const ss, char *const se, const char saved_char) {
20186         if ((!is_out && !listin) || (is_out && !listout)) {
20187           *se = saved_char;
20188           char *const s0 = ss - 4>expr._data?ss - 4:expr._data;
20189           cimg::strellipsize(s0,64);
20190           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20191                                       "CImg<%s>::%s: %s%s Invalid call with an empty image list, "
20192                                       "in expression '%s%s%s'.",
20193                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20194                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20195         }
20196       }
20197 
20198       // Check a vector is not 0-dimensional, or with unknown dimension at compile time.
20199       void check_vector0(const unsigned int dim,
20200                          char *const ss, char *const se, const char saved_char) {
20201         char *s0 = 0;
20202         if (!dim) {
20203           *se = saved_char;
20204           s0 = ss - 4>expr._data?ss - 4:expr._data;
20205           cimg::strellipsize(s0,64);
20206           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20207                                       "CImg<%s>::%s: %s%s Invalid construction of a 0-dimensional vector, "
20208                                       "in expression '%s%s%s'.",
20209                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20210                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20211         } else if (dim==~0U) {
20212           *se = saved_char;
20213           s0 = ss - 4>expr._data?ss - 4:expr._data;
20214           cimg::strellipsize(s0,64);
20215           throw CImgArgumentException("[" cimg_appname "_math_parser] "
20216                                       "CImg<%s>::%s: %s%s Invalid construction of a vector with possible dynamic size, "
20217                                       "in expression '%s%s%s'.",
20218                                       pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
20219                                       s0!=expr._data?"...":"",s0,se<&expr.back()?"...":"");
20220         }
20221       }
20222 
20223       // Evaluation functions, known by the parser.
20224       // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT),
20225       // so we can store pointers to them directly in the opcode vectors.
20226 #ifdef _mp_arg
20227 #undef _mp_arg
20228 #endif
20229 #define _mp_arg(x) mp.mem[mp.opcode[x]]
20230 
20231       static double mp_abs(_cimg_math_parser& mp) {
20232         return cimg::abs(_mp_arg(2));
20233       }
20234 
20235       static double mp_add(_cimg_math_parser& mp) {
20236         return _mp_arg(2) + _mp_arg(3);
20237       }
20238 
20239       static double mp_acos(_cimg_math_parser& mp) {
20240         return std::acos(_mp_arg(2));
20241       }
20242 
20243       static double mp_acosh(_cimg_math_parser& mp) {
20244         return cimg::acosh(_mp_arg(2));
20245       }
20246 
20247       static double mp_asinh(_cimg_math_parser& mp) {
20248         return cimg::asinh(_mp_arg(2));
20249       }
20250 
20251       static double mp_atanh(_cimg_math_parser& mp) {
20252         return cimg::atanh(_mp_arg(2));
20253       }
20254 
20255       static double mp_arg(_cimg_math_parser& mp) {
20256         const int _ind = (int)_mp_arg(4);
20257         const unsigned int
20258           nb_args = (unsigned int)mp.opcode[2] - 4,
20259           ind = _ind<0?_ind + nb_args:(unsigned int)_ind,
20260           siz = (unsigned int)mp.opcode[3];
20261         if (siz>0) {
20262           if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
20263           else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
20264           return cimg::type<double>::nan();
20265         }
20266         if (ind>=nb_args) return 0;
20267         return _mp_arg(ind + 4);
20268       }
20269 
20270       static double mp_argkth(_cimg_math_parser& mp) {
20271         const unsigned int i_end = (unsigned int)mp.opcode[2];
20272         const double val = mp_kth(mp);
20273         for (unsigned int i = 4; i<i_end; ++i) if (val==_mp_arg(i)) return i - 3.0;
20274         return 1;
20275       }
20276 
20277       static double mp_argmin(_cimg_math_parser& mp) {
20278         const unsigned int i_end = (unsigned int)mp.opcode[2];
20279         double val = _mp_arg(3);
20280         unsigned int argval = 0;
20281         for (unsigned int i = 4; i<i_end; ++i) {
20282           const double _val = _mp_arg(i);
20283           if (_val<val) { val = _val; argval = i - 3; }
20284         }
20285         return (double)argval;
20286       }
20287 
20288       static double mp_argmax(_cimg_math_parser& mp) {
20289         const unsigned int i_end = (unsigned int)mp.opcode[2];
20290         double val = _mp_arg(3);
20291         unsigned int argval = 0;
20292         for (unsigned int i = 4; i<i_end; ++i) {
20293           const double _val = _mp_arg(i);
20294           if (_val>val) { val = _val; argval = i - 3; }
20295         }
20296         return (double)argval;
20297       }
20298 
20299       static double mp_asin(_cimg_math_parser& mp) {
20300         return std::asin(_mp_arg(2));
20301       }
20302 
20303       static double mp_atan(_cimg_math_parser& mp) {
20304         return std::atan(_mp_arg(2));
20305       }
20306 
20307       static double mp_atan2(_cimg_math_parser& mp) {
20308         return std::atan2(_mp_arg(2),_mp_arg(3));
20309       }
20310 
20311       static double mp_avg(_cimg_math_parser& mp) {
20312         const unsigned int i_end = (unsigned int)mp.opcode[2];
20313         double val = _mp_arg(3);
20314         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
20315         return val/(i_end - 3);
20316       }
20317 
20318       static double mp_bitwise_and(_cimg_math_parser& mp) {
20319         return (double)((longT)_mp_arg(2) & (longT)_mp_arg(3));
20320       }
20321 
20322       static double mp_bitwise_left_shift(_cimg_math_parser& mp) {
20323         return (double)((longT)_mp_arg(2)<<(unsigned int)_mp_arg(3));
20324       }
20325 
20326       static double mp_bitwise_not(_cimg_math_parser& mp) {
20327         // Limit result to 32bits such that it can be entirely represented as a 'double'.
20328         return (double)~(unsigned int)_mp_arg(2);
20329       }
20330 
20331       static double mp_bitwise_or(_cimg_math_parser& mp) {
20332         return (double)((longT)_mp_arg(2) | (longT)_mp_arg(3));
20333       }
20334 
20335       static double mp_bitwise_right_shift(_cimg_math_parser& mp) {
20336         return (double)((longT)_mp_arg(2)>>(unsigned int)_mp_arg(3));
20337       }
20338 
20339       static double mp_bitwise_xor(_cimg_math_parser& mp) {
20340         return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3));
20341       }
20342 
20343       static double mp_bool(_cimg_math_parser& mp) {
20344         return (double)(bool)_mp_arg(2);
20345       }
20346 
20347       static double mp_break(_cimg_math_parser& mp) {
20348         mp.break_type = 1;
20349         mp.p_code = mp.p_break - 1;
20350         return cimg::type<double>::nan();
20351       }
20352 
20353       static double mp_breakpoint(_cimg_math_parser& mp) {
20354         cimg_abort_init;
20355         cimg_abort_test;
20356         cimg::unused(mp);
20357         return cimg::type<double>::nan();
20358       }
20359 
20360       static double mp_cats(_cimg_math_parser& mp) {
20361         const double *ptrd = &_mp_arg(1) + 1;
20362         const unsigned int
20363           sizd = (unsigned int)mp.opcode[2],
20364           nb_args = (unsigned int)(mp.opcode[3] - 4)/2;
20365         CImgList<charT> _str;
20366         for (unsigned int n = 0; n<nb_args; ++n) {
20367           const unsigned int siz = (unsigned int)mp.opcode[5 + 2*n];
20368           if (siz) { // Vector argument
20369             const double *ptrs = &_mp_arg(4 + 2*n) + 1;
20370             unsigned int l = 0;
20371             while (l<siz && ptrs[l]) ++l;
20372             CImg<doubleT>(ptrs,l,1,1,1,true).move_to(_str);
20373           } else CImg<charT>::vector((char)_mp_arg(4 + 2*n)).move_to(_str); // Scalar argument
20374         }
20375         CImg(1,1,1,1,0).move_to(_str);
20376         const CImg<charT> str = _str>'x';
20377         const unsigned int l = std::min(str._width,sizd);
20378         CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
20379         return cimg::type<double>::nan();
20380       }
20381 
20382       static double mp_cbrt(_cimg_math_parser& mp) {
20383         return cimg::cbrt(_mp_arg(2));
20384       }
20385 
20386       static double mp_ceil(_cimg_math_parser& mp) {
20387         return std::ceil(_mp_arg(2));
20388       }
20389 
20390       static double mp_complex_abs(_cimg_math_parser& mp) {
20391         return cimg::_hypot(_mp_arg(2),_mp_arg(3));
20392       }
20393 
20394       static double mp_complex_conj(_cimg_math_parser& mp) {
20395         const double *ptrs = &_mp_arg(2) + 1;
20396         double *ptrd = &_mp_arg(1) + 1;
20397         *(ptrd++) = *(ptrs++);
20398         *ptrd = -*(ptrs);
20399         return cimg::type<double>::nan();
20400       }
20401 
20402       static double mp_complex_div_sv(_cimg_math_parser& mp) {
20403         const double
20404           *ptr2 = &_mp_arg(3) + 1,
20405           r1 = _mp_arg(2),
20406           r2 = *(ptr2++), i2 = *ptr2;
20407         double *ptrd = &_mp_arg(1) + 1;
20408         const double denom = r2*r2 + i2*i2;
20409         *(ptrd++) = r1*r2/denom;
20410         *ptrd =  -r1*i2/denom;
20411         return cimg::type<double>::nan();
20412       }
20413 
20414       static double mp_complex_div_vv(_cimg_math_parser& mp) {
20415         const double
20416           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
20417           r1 = *(ptr1++), i1 = *ptr1,
20418           r2 = *(ptr2++), i2 = *ptr2;
20419         double *ptrd = &_mp_arg(1) + 1;
20420         const double denom = r2*r2 + i2*i2;
20421         *(ptrd++) = (r1*r2 + i1*i2)/denom;
20422         *ptrd = (r2*i1 - r1*i2)/denom;
20423         return cimg::type<double>::nan();
20424       }
20425 
20426       static double mp_complex_exp(_cimg_math_parser& mp) {
20427         double *ptrd = &_mp_arg(1) + 1;
20428         const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs), er = std::exp(r);
20429         *(ptrd++) = er*std::cos(i);
20430         *(ptrd++) = er*std::sin(i);
20431         return cimg::type<double>::nan();
20432       }
20433 
20434       static double mp_complex_log(_cimg_math_parser& mp) {
20435         double *ptrd = &_mp_arg(1) + 1;
20436         const double *ptrs = &_mp_arg(2) + 1, r = *(ptrs++), i = *(ptrs);
20437         *(ptrd++) = 0.5*std::log(r*r + i*i);
20438         *(ptrd++) = std::atan2(i,r);
20439         return cimg::type<double>::nan();
20440       }
20441 
20442       static double mp_complex_mul(_cimg_math_parser& mp) {
20443         const double
20444           *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
20445           r1 = *(ptr1++), i1 = *ptr1,
20446           r2 = *(ptr2++), i2 = *ptr2;
20447         double *ptrd = &_mp_arg(1) + 1;
20448         *(ptrd++) = r1*r2 - i1*i2;
20449         *(ptrd++) = r1*i2 + r2*i1;
20450         return cimg::type<double>::nan();
20451       }
20452 
20453       static void _mp_complex_pow(const double r1, const double i1,
20454                                   const double r2, const double i2,
20455                                   double *ptrd) {
20456         double ro, io;
20457         if (cimg::abs(i2)<1e-15) { // Exponent is real
20458           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) {
20459             if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; }
20460             else ro = io = 0;
20461           } else {
20462             const double
20463               mod1_2 = r1*r1 + i1*i1,
20464               phi1 = std::atan2(i1,r1),
20465               modo = std::pow(mod1_2,0.5*r2),
20466               phio = r2*phi1;
20467             ro = modo*std::cos(phio);
20468             io = modo*std::sin(phio);
20469           }
20470         } else { // Exponent is complex
20471           if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0;
20472           const double
20473             mod1_2 = r1*r1 + i1*i1,
20474             phi1 = std::atan2(i1,r1),
20475             modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1),
20476             phio = r2*phi1 + 0.5*i2*std::log(mod1_2);
20477           ro = modo*std::cos(phio);
20478           io = modo*std::sin(phio);
20479         }
20480         *(ptrd++) = ro;
20481         *ptrd = io;
20482       }
20483 
20484       static double mp_complex_pow_ss(_cimg_math_parser& mp) {
20485         const double val1 = _mp_arg(2), val2 = _mp_arg(3);
20486         double *ptrd = &_mp_arg(1) + 1;
20487         _mp_complex_pow(val1,0,val2,0,ptrd);
20488         return cimg::type<double>::nan();
20489       }
20490 
20491       static double mp_complex_pow_sv(_cimg_math_parser& mp) {
20492         const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1;
20493         double *ptrd = &_mp_arg(1) + 1;
20494         _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd);
20495         return cimg::type<double>::nan();
20496       }
20497 
20498       static double mp_complex_pow_vs(_cimg_math_parser& mp) {
20499         const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3);
20500         double *ptrd = &_mp_arg(1) + 1;
20501         _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd);
20502         return cimg::type<double>::nan();
20503       }
20504 
20505       static double mp_complex_pow_vv(_cimg_math_parser& mp) {
20506         const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1;
20507         double *ptrd = &_mp_arg(1) + 1;
20508         _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd);
20509         return cimg::type<double>::nan();
20510       }
20511 
20512       static double mp_continue(_cimg_math_parser& mp) {
20513         mp.break_type = 2;
20514         mp.p_code = mp.p_break - 1;
20515         return cimg::type<double>::nan();
20516       }
20517 
20518       static double mp_cos(_cimg_math_parser& mp) {
20519         return std::cos(_mp_arg(2));
20520       }
20521 
20522       static double mp_cosh(_cimg_math_parser& mp) {
20523         return std::cosh(_mp_arg(2));
20524       }
20525 
20526       static double mp_critical(_cimg_math_parser& mp) {
20527         const double res = _mp_arg(1);
20528         cimg_pragma_openmp(critical(mp_critical))
20529         {
20530           for (const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[2];
20531                mp.p_code<p_end; ++mp.p_code) { // Evaluate body
20532             mp.opcode._data = mp.p_code->_data;
20533             const ulongT target = mp.opcode[1];
20534             mp.mem[target] = _cimg_mp_defunc(mp);
20535           }
20536         }
20537         --mp.p_code;
20538         return res;
20539       }
20540 
20541       static double mp_crop(_cimg_math_parser& mp) {
20542         double *ptrd = &_mp_arg(1) + 1;
20543         const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6);
20544         const unsigned int
20545           dx = (unsigned int)mp.opcode[7],
20546           dy = (unsigned int)mp.opcode[8],
20547           dz = (unsigned int)mp.opcode[9],
20548           dc = (unsigned int)mp.opcode[10];
20549         const bool boundary_conditions = (bool)_mp_arg(11);
20550         unsigned int ind = (unsigned int)mp.opcode[2];
20551         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
20552         const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[ind];
20553         if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double));
20554         else CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c,
20555                                                                  x + dx - 1,y + dy - 1,
20556                                                                  z + dz - 1,c + dc - 1,
20557                                                                  boundary_conditions);
20558         return cimg::type<double>::nan();
20559       }
20560 
20561       static double mp_cross(_cimg_math_parser& mp) {
20562         CImg<doubleT>
20563           vout(&_mp_arg(1) + 1,1,3,1,1,true),
20564           v1(&_mp_arg(2) + 1,1,3,1,1,true),
20565           v2(&_mp_arg(3) + 1,1,3,1,1,true);
20566         (vout = v1).cross(v2);
20567         return cimg::type<double>::nan();
20568       }
20569 
20570       static double mp_cut(_cimg_math_parser& mp) {
20571         double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4);
20572         return val<cmin?cmin:val>cmax?cmax:val;
20573       }
20574 
20575       static double mp_date(_cimg_math_parser& mp) {
20576         const unsigned int
20577           _arg = (unsigned int)mp.opcode[3],
20578           _siz = (unsigned int)mp.opcode[4],
20579           siz = _siz?_siz:1;
20580         const double *const arg_in = _arg==~0U?0:&_mp_arg(3) + (_siz?1:0);
20581         double *const arg_out = &_mp_arg(1) + (_siz?1:0);
20582         if (arg_in) std::memcpy(arg_out,arg_in,siz*sizeof(double));
20583         else for (unsigned int i = 0; i<siz; ++i) arg_out[i] = i;
20584 
20585         CImg<charT> filename(mp.opcode[2] - 5);
20586         if (filename) {
20587           const ulongT *ptrs = mp.opcode._data + 5;
20588           cimg_for(filename,ptrd,char) *ptrd = (char)*(ptrs++);
20589           cimg::fdate(filename,arg_out,siz);
20590         } else cimg::date(arg_out,siz);
20591         return _siz?cimg::type<double>::nan():*arg_out;
20592       }
20593 
20594       static double mp_debug(_cimg_math_parser& mp) {
20595         CImg<charT> expr(mp.opcode[2] - 4);
20596         const ulongT *ptrs = mp.opcode._data + 4;
20597         cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
20598         cimg::strellipsize(expr);
20599         const ulongT g_target = mp.opcode[1];
20600 
20601 #ifndef cimg_use_openmp
20602         const unsigned int n_thread = 0;
20603 #else
20604         const unsigned int n_thread = omp_get_thread_num();
20605 #endif
20606         cimg_pragma_openmp(critical(mp_debug))
20607         {
20608           std::fprintf(cimg::output(),
20609                        "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
20610                        "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)",
20611                        (void*)&mp,n_thread,mp.debug_indent,' ',
20612                        expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width);
20613           std::fflush(cimg::output());
20614           mp.debug_indent+=3;
20615         }
20616         const CImg<ulongT> *const p_end = (++mp.p_code) + mp.opcode[3];
20617         CImg<ulongT> _op;
20618         for ( ; mp.p_code<p_end; ++mp.p_code) {
20619           const CImg<ulongT> &op = *mp.p_code;
20620           mp.opcode._data = op._data;
20621 
20622           _op.assign(1,op._height - 1);
20623           const ulongT *ptrs = op._data + 1;
20624           for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd<ptrde; ++ptrd)
20625             *ptrd = *(ptrs++);
20626 
20627           const ulongT target = mp.opcode[1];
20628           mp.mem[target] = _cimg_mp_defunc(mp);
20629           cimg_pragma_openmp(critical(mp_debug))
20630           {
20631             std::fprintf(cimg::output(),
20632                          "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
20633                          "Opcode %p = [ %p,%s ] -> mem[%u] = %g",
20634                          (void*)&mp,n_thread,mp.debug_indent,' ',
20635                          (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(),
20636                          (unsigned int)target,mp.mem[target]);
20637             std::fflush(cimg::output());
20638           }
20639         }
20640         cimg_pragma_openmp(critical(mp_debug))
20641         {
20642           mp.debug_indent-=3;
20643           std::fprintf(cimg::output(),
20644             "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
20645             "End debugging expression '%s' -> mem[%u] = %g (memsize: %u)",
20646             (void*)&mp,n_thread,mp.debug_indent,' ',
20647             expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width);
20648           std::fflush(cimg::output());
20649         }
20650         --mp.p_code;
20651         return mp.mem[g_target];
20652       }
20653 
20654       static double mp_decrement(_cimg_math_parser& mp) {
20655         return _mp_arg(2) - 1;
20656       }
20657 
20658       static double mp_det(_cimg_math_parser& mp) {
20659         const double *ptrs = &_mp_arg(2) + 1;
20660         const unsigned int k = (unsigned int)mp.opcode[3];
20661         return CImg<doubleT>(ptrs,k,k,1,1,true).det();
20662       }
20663 
20664       static double mp_diag(_cimg_math_parser& mp) {
20665         double *ptrd = &_mp_arg(1) + 1;
20666         const double *ptrs = &_mp_arg(2) + 1;
20667         const unsigned int k = (unsigned int)mp.opcode[3];
20668         CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptrs,1,k,1,1,true).get_diagonal();
20669         return cimg::type<double>::nan();
20670       }
20671 
20672       static double mp_display_memory(_cimg_math_parser& mp) {
20673         cimg::unused(mp);
20674         std::fputc('\n',cimg::output());
20675         mp.mem.display("[" cimg_appname "_math_parser] Memory snapshot");
20676         return cimg::type<double>::nan();
20677       }
20678 
20679       static double mp_display(_cimg_math_parser& mp) {
20680         const unsigned int
20681           _siz = (unsigned int)mp.opcode[3],
20682           siz = _siz?_siz:1;
20683         const double *const ptr = &_mp_arg(1) + (_siz?1:0);
20684         const int
20685           w = (int)_mp_arg(4),
20686           h = (int)_mp_arg(5),
20687           d = (int)_mp_arg(6),
20688           s = (int)_mp_arg(7);
20689         CImg<doubleT> img;
20690         if (w>0 && h>0 && d>0 && s>0) {
20691           if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true);
20692           else img.assign(ptr,siz).resize(w,h,d,s,-1);
20693         } else img.assign(ptr,1,siz,1,1,true);
20694 
20695         CImg<charT> expr(mp.opcode[2] - 8);
20696         const ulongT *ptrs = mp.opcode._data + 8;
20697         cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
20698         ((CImg<charT>::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr);
20699         cimg::strellipsize(expr);
20700         std::fputc('\n',cimg::output());
20701         img.display(expr._data);
20702         return cimg::type<double>::nan();
20703       }
20704 
20705       static double mp_div(_cimg_math_parser& mp) {
20706         return _mp_arg(2)/_mp_arg(3);
20707       }
20708 
20709       static double mp_dot(_cimg_math_parser& mp) {
20710         const unsigned int siz = (unsigned int)mp.opcode[4];
20711         return CImg<doubleT>(&_mp_arg(2) + 1,1,siz,1,1,true).
20712           dot(CImg<doubleT>(&_mp_arg(3) + 1,1,siz,1,1,true));
20713       }
20714 
20715       static double mp_dowhile(_cimg_math_parser& mp) {
20716         const ulongT
20717           mem_body = mp.opcode[1],
20718           mem_cond = mp.opcode[2];
20719         const CImg<ulongT>
20720           *const p_body = ++mp.p_code,
20721           *const p_cond = p_body + mp.opcode[3],
20722           *const p_end = p_cond + mp.opcode[4];
20723         const unsigned int vsiz = (unsigned int)mp.opcode[5];
20724         if (mp.opcode[6]) { // Set default value for result and condition if necessary
20725           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
20726           else mp.mem[mem_body] = cimg::type<double>::nan();
20727         }
20728         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
20729 
20730         const unsigned int _break_type = mp.break_type;
20731         mp.break_type = 0;
20732         do {
20733           for (mp.p_code = p_body; mp.p_code<p_cond; ++mp.p_code) { // Evaluate body
20734             mp.opcode._data = mp.p_code->_data;
20735             const ulongT target = mp.opcode[1];
20736             mp.mem[target] = _cimg_mp_defunc(mp);
20737           }
20738           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
20739           for (mp.p_code = p_cond; mp.p_code<p_end; ++mp.p_code) { // Evaluate condition
20740             mp.opcode._data = mp.p_code->_data;
20741             const ulongT target = mp.opcode[1];
20742             mp.mem[target] = _cimg_mp_defunc(mp);
20743           }
20744           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
20745         } while (mp.mem[mem_cond]);
20746         mp.break_type = _break_type;
20747         mp.p_code = p_end - 1;
20748         return mp.mem[mem_body];
20749       }
20750 
20751       static double mp_draw(_cimg_math_parser& mp) {
20752         const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7);
20753         unsigned int ind = (unsigned int)mp.opcode[3];
20754 
20755         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
20756         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
20757         unsigned int
20758           dx = (unsigned int)mp.opcode[8],
20759           dy = (unsigned int)mp.opcode[9],
20760           dz = (unsigned int)mp.opcode[10],
20761           dc = (unsigned int)mp.opcode[11];
20762         dx = dx==~0U?img._width:(unsigned int)_mp_arg(8);
20763         dy = dy==~0U?img._height:(unsigned int)_mp_arg(9);
20764         dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10);
20765         dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11);
20766 
20767         const ulongT sizS = mp.opcode[2];
20768         if (sizS<(ulongT)dx*dy*dz*dc)
20769           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
20770                                       "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
20771                                       "(%lu values) do not match.",
20772                                       mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
20773         CImg<doubleT> S(&_mp_arg(1) + 1,dx,dy,dz,dc,true);
20774         const float opacity = (float)_mp_arg(12);
20775 
20776         if (img._data) {
20777           if (mp.opcode[13]!=~0U) { // Opacity mask specified
20778             const ulongT sizM = mp.opcode[14];
20779             if (sizM<(ulongT)dx*dy*dz)
20780               throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
20781                                           "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
20782                                           "(%lu values) do not match.",
20783                                           mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
20784             const CImg<doubleT> M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
20785             img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15));
20786           } else img.draw_image(x,y,z,c,S,opacity);
20787         }
20788         return cimg::type<double>::nan();
20789       }
20790 
20791       static double mp_echo(_cimg_math_parser& mp) {
20792         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
20793         CImgList<charT> _str;
20794         CImg<charT> it;
20795         for (unsigned int n = 0; n<nb_args; ++n) {
20796           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
20797           if (siz) { // Vector argument -> string
20798             const double *ptr = &_mp_arg(3 + 2*n) + 1;
20799             unsigned int l = 0;
20800             while (l<siz && ptr[l]) ++l;
20801             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
20802           } else { // Scalar argument -> number
20803             it.assign(256);
20804             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
20805             CImg<charT>::string(it,false,true).move_to(_str);
20806           }
20807         }
20808         CImg(1,1,1,1,0).move_to(_str);
20809         const CImg<charT> str = _str>'x';
20810         std::fprintf(cimg::output(),"\n%s",str._data);
20811         return cimg::type<double>::nan();
20812       }
20813 
20814       static double mp_ellipse(_cimg_math_parser& mp) {
20815         const unsigned int i_end = (unsigned int)mp.opcode[2];
20816         unsigned int ind = (unsigned int)mp.opcode[3];
20817         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
20818         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
20819         CImg<T> color(img._spectrum,1,1,1,0);
20820         bool is_invalid_arguments = false;
20821         unsigned int i = 4;
20822         float r1 = 0, r2 = 0, angle = 0, opacity = 1;
20823         int x0 = 0, y0 = 0;
20824         if (i>=i_end) is_invalid_arguments = true;
20825         else {
20826           x0 = (int)cimg::round(_mp_arg(i++));
20827           if (i>=i_end) is_invalid_arguments = true;
20828           else {
20829             y0 = (int)cimg::round(_mp_arg(i++));
20830             if (i>=i_end) is_invalid_arguments = true;
20831             else {
20832               r1 = (float)_mp_arg(i++);
20833               if (i>=i_end) r2 = r1;
20834               else {
20835                 r2 = (float)_mp_arg(i++);
20836                 if (i<i_end) {
20837                   angle = (float)_mp_arg(i++);
20838                   if (i<i_end) {
20839                     opacity = (float)_mp_arg(i++);
20840                     if (i<i_end) {
20841                       cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
20842                       else { color.resize(k,1,1,1,-1); break; }
20843                       color.resize(img._spectrum,1,1,1,0,2);
20844                     }
20845                   }
20846                 }
20847               }
20848             }
20849           }
20850         }
20851         if (!is_invalid_arguments) img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity);
20852         else {
20853           CImg<doubleT> args(i_end - 4);
20854           cimg_forX(args,k) args[k] = _mp_arg(4 + k);
20855           if (ind==~0U)
20856             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
20857                                         "Invalid arguments '%s'. ",
20858                                         mp.imgin.pixel_type(),args.value_string()._data);
20859           else
20860             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
20861                                         "Invalid arguments '#%u%s%s'. ",
20862                                         mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
20863         }
20864         return cimg::type<double>::nan();
20865       }
20866 
20867       static double mp_eq(_cimg_math_parser& mp) {
20868         return (double)(_mp_arg(2)==_mp_arg(3));
20869       }
20870 
20871       static double mp_ext(_cimg_math_parser& mp) {
20872         const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
20873         CImgList<charT> _str;
20874         CImg<charT> it;
20875         for (unsigned int n = 0; n<nb_args; ++n) {
20876           const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
20877           if (siz) { // Vector argument -> string
20878             const double *ptr = &_mp_arg(3 + 2*n) + 1;
20879             unsigned int l = 0;
20880             while (l<siz && ptr[l]) ++l;
20881             CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
20882           } else { // Scalar argument -> number
20883             it.assign(256);
20884             cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
20885             CImg<charT>::string(it,false,true).move_to(_str);
20886           }
20887         }
20888         CImg(1,1,1,1,0).move_to(_str);
20889         CImg<charT> str = _str>'x';
20890 #ifdef cimg_mp_ext_function
20891         cimg_mp_ext_function(str);
20892 #endif
20893         return cimg::type<double>::nan();
20894       }
20895 
20896       static double mp_exp(_cimg_math_parser& mp) {
20897         return std::exp(_mp_arg(2));
20898       }
20899 
20900       static double mp_eye(_cimg_math_parser& mp) {
20901         double *ptrd = &_mp_arg(1) + 1;
20902         const unsigned int k = (unsigned int)mp.opcode[2];
20903         CImg<doubleT>(ptrd,k,k,1,1,true).identity_matrix();
20904         return cimg::type<double>::nan();
20905       }
20906 
20907       static double mp_factorial(_cimg_math_parser& mp) {
20908         return cimg::factorial((int)_mp_arg(2));
20909       }
20910 
20911       static double mp_fibonacci(_cimg_math_parser& mp) {
20912         return cimg::fibonacci((int)_mp_arg(2));
20913       }
20914 
20915       static double mp_find(_cimg_math_parser& mp) {
20916         const bool is_forward = (bool)_mp_arg(5);
20917         const ulongT siz = (ulongT)mp.opcode[3];
20918         longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):is_forward?0:siz - 1);
20919         if (ind<0 || ind>=(longT)siz) return -1.;
20920         const double
20921           *const ptrb = &_mp_arg(2) + 1,
20922           *const ptre = ptrb + siz,
20923           val = _mp_arg(4),
20924           *ptr = ptrb + ind;
20925 
20926         // Forward search
20927         if (is_forward) {
20928           while (ptr<ptre && *ptr!=val) ++ptr;
20929           return ptr==ptre?-1.:(double)(ptr - ptrb);
20930         }
20931 
20932         // Backward search.
20933         while (ptr>=ptrb && *ptr!=val) --ptr;
20934         return ptr<ptrb?-1.:(double)(ptr - ptrb);
20935       }
20936 
20937       static double mp_find_seq(_cimg_math_parser& mp) {
20938         const bool is_forward = (bool)_mp_arg(6);
20939         const ulongT
20940           siz1 = (ulongT)mp.opcode[3],
20941           siz2 = (ulongT)mp.opcode[5];
20942         longT ind = (longT)(mp.opcode[7]!=_cimg_mp_slot_nan?_mp_arg(7):is_forward?0:siz1 - 1);
20943         if (ind<0 || ind>=(longT)siz1) return -1.;
20944         const double
20945           *const ptr1b = &_mp_arg(2) + 1,
20946           *const ptr1e = ptr1b + siz1,
20947           *const ptr2b = &_mp_arg(4) + 1,
20948           *const ptr2e = ptr2b + siz2,
20949           *ptr1 = ptr1b + ind,
20950           *p1 = 0,
20951           *p2 = 0;
20952 
20953         // Forward search.
20954         if (is_forward) {
20955           do {
20956             while (ptr1<ptr1e && *ptr1!=*ptr2b) ++ptr1;
20957             p1 = ptr1 + 1;
20958             p2 = ptr2b + 1;
20959             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
20960           } while (p2<ptr2e && ++ptr1<ptr1e);
20961           return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
20962         }
20963 
20964         // Backward search.
20965         do {
20966           while (ptr1>=ptr1b && *ptr1!=*ptr2b) --ptr1;
20967           p1 = ptr1 + 1;
20968           p2 = ptr2b + 1;
20969           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
20970         } while (p2<ptr2e && --ptr1>=ptr1b);
20971         return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
20972       }
20973 
20974       static double mp_floor(_cimg_math_parser& mp) {
20975         return std::floor(_mp_arg(2));
20976       }
20977 
20978       static double mp_for(_cimg_math_parser& mp) {
20979         const ulongT
20980           mem_body = mp.opcode[1],
20981           mem_cond = mp.opcode[3];
20982         const CImg<ulongT>
20983           *const p_init = ++mp.p_code,
20984           *const p_cond = p_init + mp.opcode[4],
20985           *const p_body = p_cond + mp.opcode[5],
20986           *const p_post = p_body + mp.opcode[6],
20987           *const p_end = p_post + mp.opcode[7];
20988         const unsigned int vsiz = (unsigned int)mp.opcode[2];
20989         bool is_cond = false;
20990         if (mp.opcode[8]) { // Set default value for result and condition if necessary
20991           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
20992           else mp.mem[mem_body] = cimg::type<double>::nan();
20993         }
20994         if (mp.opcode[9]) mp.mem[mem_cond] = 0;
20995         const unsigned int _break_type = mp.break_type;
20996         mp.break_type = 0;
20997 
20998         for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init
20999           mp.opcode._data = mp.p_code->_data;
21000           const ulongT target = mp.opcode[1];
21001           mp.mem[target] = _cimg_mp_defunc(mp);
21002         }
21003 
21004         if (!mp.break_type) do {
21005             for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
21006               mp.opcode._data = mp.p_code->_data;
21007               const ulongT target = mp.opcode[1];
21008               mp.mem[target] = _cimg_mp_defunc(mp);
21009             }
21010             if (mp.break_type==1) break;
21011 
21012             is_cond = (bool)mp.mem[mem_cond];
21013             if (is_cond && !mp.break_type) {
21014               for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body
21015                 mp.opcode._data = mp.p_code->_data;
21016                 const ulongT target = mp.opcode[1];
21017                 mp.mem[target] = _cimg_mp_defunc(mp);
21018               }
21019               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
21020 
21021               for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code
21022                 mp.opcode._data = mp.p_code->_data;
21023                 const ulongT target = mp.opcode[1];
21024                 mp.mem[target] = _cimg_mp_defunc(mp);
21025               }
21026               if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
21027             }
21028           } while (is_cond);
21029 
21030         mp.break_type = _break_type;
21031         mp.p_code = p_end - 1;
21032         return mp.mem[mem_body];
21033       }
21034 
21035       static double mp_fsize(_cimg_math_parser& mp) {
21036         const CImg<charT> filename(mp.opcode._data + 3,mp.opcode[2] - 3);
21037         return (double)cimg::fsize(filename);
21038       }
21039 
21040       static double mp_g(_cimg_math_parser& mp) {
21041         cimg::unused(mp);
21042         return cimg::grand();
21043       }
21044 
21045       static double mp_gauss(_cimg_math_parser& mp) {
21046         const double x = _mp_arg(2), s = _mp_arg(3);
21047         return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1);
21048       }
21049 
21050       static double mp_gcd(_cimg_math_parser& mp) {
21051         return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3));
21052       }
21053 
21054       static double mp_gt(_cimg_math_parser& mp) {
21055         return (double)(_mp_arg(2)>_mp_arg(3));
21056       }
21057 
21058       static double mp_gte(_cimg_math_parser& mp) {
21059         return (double)(_mp_arg(2)>=_mp_arg(3));
21060       }
21061 
21062       static double mp_i(_cimg_math_parser& mp) {
21063         return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y],
21064                                        (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0);
21065       }
21066 
21067       static double mp_if(_cimg_math_parser& mp) {
21068         const bool is_cond = (bool)_mp_arg(2);
21069         const ulongT
21070           mem_left = mp.opcode[3],
21071           mem_right = mp.opcode[4];
21072         const CImg<ulongT>
21073           *const p_right = ++mp.p_code + mp.opcode[5],
21074           *const p_end = p_right + mp.opcode[6];
21075         const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7];
21076         if (is_cond) for ( ; mp.p_code<p_right; ++mp.p_code) {
21077             mp.opcode._data = mp.p_code->_data;
21078             const ulongT target = mp.opcode[1];
21079             mp.mem[target] = _cimg_mp_defunc(mp);
21080           }
21081         else for (mp.p_code = p_right; mp.p_code<p_end; ++mp.p_code) {
21082             mp.opcode._data = mp.p_code->_data;
21083             const ulongT target = mp.opcode[1];
21084             mp.mem[target] = _cimg_mp_defunc(mp);
21085           }
21086         if (mp.p_code==mp.p_break) --mp.p_code;
21087         else mp.p_code = p_end - 1;
21088         if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz);
21089         return mp.mem[is_cond?mem_left:mem_right];
21090       }
21091 
21092       static double mp_image_d(_cimg_math_parser& mp) {
21093         unsigned int ind = (unsigned int)mp.opcode[2];
21094         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21095         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21096         return (double)img.depth();
21097       }
21098 
21099       static double mp_image_display(_cimg_math_parser& mp) {
21100         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
21101         cimg::mutex(6);
21102         CImg<T> &img = mp.listout[ind];
21103         CImg<charT> title(256);
21104         std::fputc('\n',cimg::output());
21105         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
21106         img.display(title);
21107         cimg::mutex(6,0);
21108         return cimg::type<double>::nan();
21109       }
21110 
21111       static double mp_image_h(_cimg_math_parser& mp) {
21112         unsigned int ind = (unsigned int)mp.opcode[2];
21113         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21114         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21115         return (double)img.height();
21116       }
21117 
21118       static double mp_image_median(_cimg_math_parser& mp) {
21119         unsigned int ind = (unsigned int)mp.opcode[2];
21120         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21121         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21122         return (double)img.median();
21123       }
21124 
21125       static double mp_image_print(_cimg_math_parser& mp) {
21126         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
21127         cimg::mutex(6);
21128         CImg<T> &img = mp.listout[ind];
21129         CImg<charT> title(256);
21130         std::fputc('\n',cimg::output());
21131         cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
21132         img.print(title);
21133         cimg::mutex(6,0);
21134         return cimg::type<double>::nan();
21135       }
21136 
21137       static double mp_image_resize(_cimg_math_parser& mp) {
21138         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width());
21139         cimg::mutex(6);
21140         CImg<T> &img = mp.listout[ind];
21141         const double
21142           _w = mp.opcode[3]==~0U?-100:_mp_arg(3),
21143           _h = mp.opcode[4]==~0U?-100:_mp_arg(4),
21144           _d = mp.opcode[5]==~0U?-100:_mp_arg(5),
21145           _s = mp.opcode[6]==~0U?-100:_mp_arg(6);
21146         const unsigned int
21147           w = (unsigned int)(_w>=0?_w:-_w*img.width()/100),
21148           h = (unsigned int)(_h>=0?_h:-_h*img.height()/100),
21149           d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100),
21150           s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100),
21151           interp = (int)_mp_arg(7);
21152         if (mp.is_fill && img._data==mp.imgout._data) {
21153           cimg::mutex(6,0);
21154           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': "
21155                                       "Cannot both fill and resize image (%u,%u,%u,%u) "
21156                                       "to new dimensions (%u,%u,%u,%u).",
21157                                       img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s);
21158         }
21159         const unsigned int
21160           boundary = (int)_mp_arg(8);
21161         const float
21162           cx = (float)_mp_arg(9),
21163           cy = (float)_mp_arg(10),
21164           cz = (float)_mp_arg(11),
21165           cc = (float)_mp_arg(12);
21166         img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc);
21167         cimg::mutex(6,0);
21168         return cimg::type<double>::nan();
21169       }
21170 
21171       static double mp_image_s(_cimg_math_parser& mp) {
21172         unsigned int ind = (unsigned int)mp.opcode[2];
21173         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21174         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21175         return (double)img.spectrum();
21176       }
21177 
21178       static double mp_image_sort(_cimg_math_parser& mp) {
21179         const bool is_increasing = (bool)_mp_arg(3);
21180         const unsigned int
21181           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()),
21182           axis = (unsigned int)_mp_arg(4);
21183         cimg::mutex(6);
21184         CImg<T> &img = mp.listout[ind];
21185         img.sort(is_increasing,
21186                  axis==0 || axis=='x'?'x':
21187                  axis==1 || axis=='y'?'y':
21188                  axis==2 || axis=='z'?'z':
21189                  axis==3 || axis=='c'?'c':0);
21190         cimg::mutex(6,0);
21191         return cimg::type<double>::nan();
21192       }
21193 
21194       static double mp_image_stats(_cimg_math_parser& mp) {
21195         double *ptrd = &_mp_arg(1) + 1;
21196         unsigned int ind = (unsigned int)mp.opcode[2];
21197         if (ind==~0U) CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imgout.get_stats();
21198         else {
21199           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21200           CImg<doubleT>(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats();
21201         }
21202         return cimg::type<double>::nan();
21203       }
21204 
21205       static double mp_image_w(_cimg_math_parser& mp) {
21206         unsigned int ind = (unsigned int)mp.opcode[2];
21207         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21208         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21209         return (double)img.width();
21210       }
21211 
21212       static double mp_image_wh(_cimg_math_parser& mp) {
21213         unsigned int ind = (unsigned int)mp.opcode[2];
21214         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21215         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21216         return (double)img.width()*img.height();
21217       }
21218 
21219       static double mp_image_whd(_cimg_math_parser& mp) {
21220         unsigned int ind = (unsigned int)mp.opcode[2];
21221         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21222         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21223         return (double)img.width()*img.height()*img.depth();
21224       }
21225 
21226       static double mp_image_whds(_cimg_math_parser& mp) {
21227         unsigned int ind = (unsigned int)mp.opcode[2];
21228         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21229         const CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
21230         return (double)img.width()*img.height()*img.depth()*img.spectrum();
21231       }
21232 
21233       static double mp_increment(_cimg_math_parser& mp) {
21234         return _mp_arg(2) + 1;
21235       }
21236 
21237       static double mp_int(_cimg_math_parser& mp) {
21238         return (double)(longT)_mp_arg(2);
21239       }
21240 
21241       static double mp_ioff(_cimg_math_parser& mp) {
21242         const unsigned int
21243           boundary_conditions = (unsigned int)_mp_arg(3);
21244         const CImg<T> &img = mp.imgin;
21245         const longT
21246           off = (longT)_mp_arg(2),
21247           whds = (longT)img.size();
21248         if (off>=0 && off<whds) return (double)img[off];
21249         if (img._data) switch (boundary_conditions) {
21250           case 3 : { // Mirror
21251             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
21252             return (double)img[moff<whds?moff:whds2 - moff - 1];
21253           }
21254           case 2 : // Periodic
21255             return (double)img[cimg::mod(off,whds)];
21256           case 1 : // Neumann
21257             return (double)img[off<0?0:whds - 1];
21258           default : // Dirichlet
21259             return 0;
21260           }
21261         return 0;
21262       }
21263 
21264       static double mp_isbool(_cimg_math_parser& mp) {
21265         const double val = _mp_arg(2);
21266         return (double)(val==0.0 || val==1.0);
21267       }
21268 
21269       static double mp_isin(_cimg_math_parser& mp) {
21270         const unsigned int i_end = (unsigned int)mp.opcode[2];
21271         const double val = _mp_arg(3);
21272         for (unsigned int i = 4; i<i_end; ++i)
21273           if (val==_mp_arg(i)) return 1.0;
21274         return 0.0;
21275       }
21276 
21277       static double mp_isinf(_cimg_math_parser& mp) {
21278         return (double)cimg::type<double>::is_inf(_mp_arg(2));
21279       }
21280 
21281       static double mp_isint(_cimg_math_parser& mp) {
21282         return (double)(cimg::mod(_mp_arg(2),1.0)==0);
21283       }
21284 
21285       static double mp_isnan(_cimg_math_parser& mp) {
21286         return (double)cimg::type<double>::is_nan(_mp_arg(2));
21287       }
21288 
21289       static double mp_ixyzc(_cimg_math_parser& mp) {
21290         const unsigned int
21291           interpolation = (unsigned int)_mp_arg(6),
21292           boundary_conditions = (unsigned int)_mp_arg(7);
21293         const CImg<T> &img = mp.imgin;
21294         const double
21295           x = _mp_arg(2), y = _mp_arg(3),
21296           z = _mp_arg(4), c = _mp_arg(5);
21297         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21298           case 3 : { // Mirror
21299             const int
21300               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
21301               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
21302               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
21303             return (double)img(mx<img.width()?mx:w2 - mx - 1,
21304                                my<img.height()?my:h2 - my - 1,
21305                                mz<img.depth()?mz:d2 - mz - 1,
21306                                mc<img.spectrum()?mc:s2 - mc - 1);
21307           }
21308           case 2 : // Periodic
21309             return (double)img(cimg::mod((int)x,img.width()),
21310                                cimg::mod((int)y,img.height()),
21311                                cimg::mod((int)z,img.depth()),
21312                                cimg::mod((int)c,img.spectrum()));
21313           case 1 : // Neumann
21314             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
21315           default : // Dirichlet
21316             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
21317           } else switch (boundary_conditions) { // Linear interpolation
21318           case 3 : { // Mirror
21319             const float
21320               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
21321               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
21322               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
21323             return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
21324                                               my<img.height()?my:h2 - my - 1,
21325                                               mz<img.depth()?mz:d2 - mz - 1,
21326                                               mc<img.spectrum()?mc:s2 - mc - 1);
21327           }
21328           case 2 : // Periodic
21329             return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
21330                                               cimg::mod((float)y,(float)img.height()),
21331                                               cimg::mod((float)z,(float)img.depth()),
21332                                               cimg::mod((float)c,(float)img.spectrum()));
21333           case 1 : // Neumann
21334             return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
21335           default : // Dirichlet
21336             return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
21337           }
21338       }
21339 
21340       static double mp_joff(_cimg_math_parser& mp) {
21341         const unsigned int
21342           boundary_conditions = (unsigned int)_mp_arg(3);
21343         const int
21344           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21345           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21346         const CImg<T> &img = mp.imgin;
21347         const longT
21348           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
21349           whds = (longT)img.size();
21350         if (off>=0 && off<whds) return (double)img[off];
21351         if (img._data) switch (boundary_conditions) {
21352           case 3 : { // Mirror
21353             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
21354             return (double)img[moff<whds?moff:whds2 - moff - 1];
21355           }
21356           case 2 : // Periodic
21357             return (double)img[cimg::mod(off,whds)];
21358           case 1 : // Neumann
21359             return (double)img[off<0?0:whds - 1];
21360           default : // Dirichlet
21361             return 0;
21362           }
21363         return 0;
21364       }
21365 
21366       static double mp_jxyzc(_cimg_math_parser& mp) {
21367         const unsigned int
21368           interpolation = (unsigned int)_mp_arg(6),
21369           boundary_conditions = (unsigned int)_mp_arg(7);
21370         const CImg<T> &img = mp.imgin;
21371         const double
21372           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
21373           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
21374           x = ox + _mp_arg(2), y = oy + _mp_arg(3),
21375           z = oz + _mp_arg(4), c = oc + _mp_arg(5);
21376         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21377           case 3 : { // Mirror
21378             const int
21379               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
21380               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
21381               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
21382             return (double)img(mx<img.width()?mx:w2 - mx - 1,
21383                                my<img.height()?my:h2 - my - 1,
21384                                mz<img.depth()?mz:d2 - mz - 1,
21385                                mc<img.spectrum()?mc:s2 - mc - 1);
21386           }
21387           case 2 : // Periodic
21388             return (double)img(cimg::mod((int)x,img.width()),
21389                                cimg::mod((int)y,img.height()),
21390                                cimg::mod((int)z,img.depth()),
21391                                cimg::mod((int)c,img.spectrum()));
21392           case 1 : // Neumann
21393             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
21394           default : // Dirichlet
21395             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
21396           } else switch (boundary_conditions) { // Linear interpolation
21397           case 3 : { // Mirror
21398             const float
21399               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
21400               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
21401               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
21402             return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
21403                                               my<img.height()?my:h2 - my - 1,
21404                                               mz<img.depth()?mz:d2 - mz - 1,
21405                                               mc<img.spectrum()?mc:s2 - mc - 1);
21406           }
21407           case 2 : // Periodic
21408             return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
21409                                               cimg::mod((float)y,(float)img.height()),
21410                                               cimg::mod((float)z,(float)img.depth()),
21411                                               cimg::mod((float)c,(float)img.spectrum()));
21412           case 1 : // Neumann
21413             return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
21414           default : // Dirichlet
21415             return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
21416           }
21417       }
21418 
21419       static double mp_kth(_cimg_math_parser& mp) {
21420         const unsigned int i_end = (unsigned int)mp.opcode[2];
21421         CImg<doubleT> vals(i_end - 4);
21422         double *p = vals.data();
21423         for (unsigned int i = 4; i<i_end; ++i) *(p++) = _mp_arg(i);
21424         int ind = (int)cimg::round(_mp_arg(3));
21425         if (ind<0) ind+=vals.width() + 1;
21426         ind = std::max(1,std::min(vals.width(),ind));
21427         return vals.kth_smallest(ind - 1);
21428       }
21429 
21430       static double mp_linear_add(_cimg_math_parser& mp) {
21431         return _mp_arg(2)*_mp_arg(3) + _mp_arg(4);
21432       }
21433 
21434       static double mp_linear_sub_left(_cimg_math_parser& mp) {
21435         return _mp_arg(2)*_mp_arg(3) - _mp_arg(4);
21436       }
21437 
21438       static double mp_linear_sub_right(_cimg_math_parser& mp) {
21439         return _mp_arg(4) - _mp_arg(2)*_mp_arg(3);
21440       }
21441 
21442       static double mp_list_depth(_cimg_math_parser& mp) {
21443         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21444         return (double)mp.listin[ind]._depth;
21445       }
21446 
21447       static double mp_list_find(_cimg_math_parser& mp) {
21448         const unsigned int
21449           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21450         const CImg<T> &img = mp.listin[indi];
21451         const bool is_forward = (bool)_mp_arg(4);
21452         const ulongT siz = (ulongT)img.size();
21453         longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):is_forward?0:siz - 1);
21454         if (ind<0 || ind>=(longT)siz) return -1.;
21455         const T
21456           *const ptrb = img.data(),
21457           *const ptre = img.end(),
21458           *ptr = ptrb + ind;
21459         const double val = _mp_arg(3);
21460 
21461         // Forward search
21462         if (is_forward) {
21463           while (ptr<ptre && (double)*ptr!=val) ++ptr;
21464           return ptr==ptre?-1.:(double)(ptr - ptrb);
21465         }
21466 
21467         // Backward search.
21468         while (ptr>=ptrb && (double)*ptr!=val) --ptr;
21469         return ptr<ptrb?-1.:(double)(ptr - ptrb);
21470       }
21471 
21472       static double mp_list_find_seq(_cimg_math_parser& mp) {
21473         const unsigned int
21474           indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21475         const CImg<T> &img = mp.listin[indi];
21476         const bool is_forward = (bool)_mp_arg(5);
21477         const ulongT
21478           siz1 = (ulongT)img.size(),
21479           siz2 = (ulongT)mp.opcode[4];
21480         longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):is_forward?0:siz1 - 1);
21481         if (ind<0 || ind>=(longT)siz1) return -1.;
21482         const T
21483           *const ptr1b = img.data(),
21484           *const ptr1e = ptr1b + siz1,
21485           *ptr1 = ptr1b + ind,
21486           *p1 = 0;
21487         const double
21488           *const ptr2b = &_mp_arg(3) + 1,
21489           *const ptr2e = ptr2b + siz2,
21490           *p2 = 0;
21491 
21492         // Forward search.
21493         if (is_forward) {
21494           do {
21495             while (ptr1<ptr1e && *ptr1!=*ptr2b) ++ptr1;
21496             p1 = ptr1 + 1;
21497             p2 = ptr2b + 1;
21498             while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
21499           } while (p2<ptr2e && ++ptr1<ptr1e);
21500           return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
21501         }
21502 
21503         // Backward search.
21504         do {
21505           while (ptr1>=ptr1b && *ptr1!=*ptr2b) --ptr1;
21506           p1 = ptr1 + 1;
21507           p2 = ptr2b + 1;
21508           while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
21509         } while (p2<ptr2e && --ptr1>=ptr1b);
21510         return p2<ptr2e?-1.0:(double)(ptr1 - ptr1b);
21511       }
21512 
21513       static double mp_list_height(_cimg_math_parser& mp) {
21514         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21515         return (double)mp.listin[ind]._height;
21516       }
21517 
21518       static double mp_list_ioff(_cimg_math_parser& mp) {
21519         const unsigned int
21520           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21521           boundary_conditions = (unsigned int)_mp_arg(4);
21522         const CImg<T> &img = mp.listin[ind];
21523         const longT
21524           off = (longT)_mp_arg(3),
21525           whds = (longT)img.size();
21526         if (off>=0 && off<whds) return (double)img[off];
21527         if (img._data) switch (boundary_conditions) {
21528           case 3 : { // Mirror
21529             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
21530             return (double)img[moff<whds?moff:whds2 - moff - 1];
21531           }
21532           case 2 : // Periodic
21533             return (double)img[cimg::mod(off,whds)];
21534           case 1 : // Neumann
21535             return (double)img[off<0?0:whds - 1];
21536           default : // Dirichlet
21537             return 0;
21538           }
21539         return 0;
21540       }
21541 
21542       static double mp_list_is_shared(_cimg_math_parser& mp) {
21543         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21544         return (double)mp.listin[ind]._is_shared;
21545       }
21546 
21547       static double mp_list_ixyzc(_cimg_math_parser& mp) {
21548         const unsigned int
21549           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21550           interpolation = (unsigned int)_mp_arg(7),
21551           boundary_conditions = (unsigned int)_mp_arg(8);
21552         const CImg<T> &img = mp.listin[ind];
21553         const double
21554           x = _mp_arg(3), y = _mp_arg(4),
21555           z = _mp_arg(5), c = _mp_arg(6);
21556         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21557           case 3 : { // Mirror
21558             const int
21559               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
21560               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
21561               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
21562             return (double)img(mx<img.width()?mx:w2 - mx - 1,
21563                                my<img.height()?my:h2 - my - 1,
21564                                mz<img.depth()?mz:d2 - mz - 1,
21565                                mc<img.spectrum()?mc:s2 - mc - 1);
21566           }
21567           case 2 : // Periodic
21568             return (double)img(cimg::mod((int)x,img.width()),
21569                                cimg::mod((int)y,img.height()),
21570                                cimg::mod((int)z,img.depth()),
21571                                cimg::mod((int)c,img.spectrum()));
21572           case 1 : // Neumann
21573             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
21574           default : // Dirichlet
21575             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
21576           } else switch (boundary_conditions) { // Linear interpolation
21577           case 3 : { // Mirror
21578             const float
21579               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
21580               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
21581               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
21582             return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
21583                                               my<img.height()?my:h2 - my - 1,
21584                                               mz<img.depth()?mz:d2 - mz - 1,
21585                                               mc<img.spectrum()?mc:s2 - mc - 1);
21586           }
21587           case 2 : // Periodic
21588             return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
21589                                               cimg::mod((float)y,(float)img.height()),
21590                                               cimg::mod((float)z,(float)img.depth()),
21591                                               cimg::mod((float)c,(float)img.spectrum()));
21592           case 1 : // Neumann
21593             return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
21594           default : // Dirichlet
21595             return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
21596           }
21597       }
21598 
21599       static double mp_list_joff(_cimg_math_parser& mp) {
21600         const unsigned int
21601           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21602           boundary_conditions = (unsigned int)_mp_arg(4);
21603         const int
21604           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21605           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21606         const CImg<T> &img = mp.listin[ind];
21607         const longT
21608           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
21609           whds = (longT)img.size();
21610         if (off>=0 && off<whds) return (double)img[off];
21611         if (img._data) switch (boundary_conditions) {
21612           case 3 : { // Mirror
21613             const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
21614             return (double)img[moff<whds?moff:whds2 - moff - 1];
21615           }
21616           case 2 : // Periodic
21617             return (double)img[cimg::mod(off,whds)];
21618           case 1 : // Neumann
21619             return (double)img[off<0?0:whds - 1];
21620           default : // Dirichlet
21621             return 0;
21622           }
21623         return 0;
21624       }
21625 
21626       static double mp_list_jxyzc(_cimg_math_parser& mp) {
21627         const unsigned int
21628           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21629           interpolation = (unsigned int)_mp_arg(7),
21630           boundary_conditions = (unsigned int)_mp_arg(8);
21631         const CImg<T> &img = mp.listin[ind];
21632         const double
21633           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
21634           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
21635           x = ox + _mp_arg(3), y = oy + _mp_arg(4),
21636           z = oz + _mp_arg(5), c = oc + _mp_arg(6);
21637         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21638           case 3 : { // Mirror
21639             const int
21640               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
21641               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
21642               mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
21643             return (double)img(mx<img.width()?mx:w2 - mx - 1,
21644                                my<img.height()?my:h2 - my - 1,
21645                                mz<img.depth()?mz:d2 - mz - 1,
21646                                mc<img.spectrum()?mc:s2 - mc - 1);
21647           }
21648           case 2 : // Periodic
21649             return (double)img(cimg::mod((int)x,img.width()),
21650                                cimg::mod((int)y,img.height()),
21651                                cimg::mod((int)z,img.depth()),
21652                                cimg::mod((int)c,img.spectrum()));
21653           case 1 : // Neumann
21654             return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
21655           default : // Dirichlet
21656             return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
21657           } else switch (boundary_conditions) { // Linear interpolation
21658           case 3 : { // Mirror
21659             const float
21660               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(), s2 = 2.0f*img.spectrum(),
21661               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
21662               mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
21663             return (double)img._linear_atXYZC(mx<img.width()?mx:w2 - mx - 1,
21664                                               my<img.height()?my:h2 - my - 1,
21665                                               mz<img.depth()?mz:d2 - mz - 1,
21666                                               mc<img.spectrum()?mc:s2 - mc - 1);
21667           }
21668           case 2 : // Periodic
21669             return (double)img._linear_atXYZC(cimg::mod((float)x,(float)img.width()),
21670                                               cimg::mod((float)y,(float)img.height()),
21671                                               cimg::mod((float)z,(float)img.depth()),
21672                                               cimg::mod((float)c,(float)img.spectrum()));
21673           case 1 : // Neumann
21674             return (double)img._linear_atXYZC((float)x,(float)y,(float)z,(float)c);
21675           default : // Dirichlet
21676             return (double)img.linear_atXYZC((float)x,(float)y,(float)z,(float)c,(T)0);
21677           }
21678       }
21679 
21680       static double mp_list_l(_cimg_math_parser& mp) {
21681         return (double)mp.listout.width();
21682       }
21683 
21684       static double mp_list_median(_cimg_math_parser& mp) {
21685         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21686         if (!mp.list_median) mp.list_median.assign(mp.listin._width);
21687         if (!mp.list_median[ind]) CImg<doubleT>::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]);
21688         return *mp.list_median[ind];
21689       }
21690 
21691       static double mp_list_set_ioff(_cimg_math_parser& mp) {
21692         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21693         CImg<T> &img = mp.listout[ind];
21694         const longT
21695           off = (longT)_mp_arg(3),
21696           whds = (longT)img.size();
21697         const double val = _mp_arg(1);
21698         if (off>=0 && off<whds) img[off] = (T)val;
21699         return val;
21700       }
21701 
21702       static double mp_list_set_ixyzc(_cimg_math_parser& mp) {
21703         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21704         CImg<T> &img = mp.listout[ind];
21705         const int
21706           x = (int)_mp_arg(3), y = (int)_mp_arg(4),
21707           z = (int)_mp_arg(5), c = (int)_mp_arg(6);
21708         const double val = _mp_arg(1);
21709         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
21710             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
21711           img(x,y,z,c) = (T)val;
21712         return val;
21713       }
21714 
21715       static double mp_list_set_joff(_cimg_math_parser& mp) {
21716         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21717         CImg<T> &img = mp.listout[ind];
21718         const int
21719           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21720           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21721         const longT
21722           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
21723           whds = (longT)img.size();
21724         const double val = _mp_arg(1);
21725         if (off>=0 && off<whds) img[off] = (T)val;
21726         return val;
21727       }
21728 
21729       static double mp_list_set_jxyzc(_cimg_math_parser& mp) {
21730         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21731         CImg<T> &img = mp.listout[ind];
21732         const double
21733           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
21734           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
21735         const int
21736           x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)),
21737           z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6));
21738         const double val = _mp_arg(1);
21739         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
21740             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
21741           img(x,y,z,c) = (T)val;
21742         return val;
21743       }
21744 
21745       static double mp_list_set_Ioff_s(_cimg_math_parser& mp) {
21746         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21747         CImg<T> &img = mp.listout[ind];
21748         const longT
21749           off = (longT)_mp_arg(3),
21750           whd = (longT)img.width()*img.height()*img.depth();
21751         const T val = (T)_mp_arg(1);
21752         if (off>=0 && off<whd) {
21753           T *ptrd = &img[off];
21754           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
21755         }
21756         return _mp_arg(1);
21757       }
21758 
21759       static double mp_list_set_Ioff_v(_cimg_math_parser& mp) {
21760         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21761         CImg<T> &img = mp.listout[ind];
21762         const longT
21763           off = (longT)_mp_arg(3),
21764           whd = (longT)img.width()*img.height()*img.depth();
21765         const double *ptrs = &_mp_arg(1) + 1;
21766         if (off>=0 && off<whd) {
21767           const unsigned int vsiz = (unsigned int)mp.opcode[4];
21768           T *ptrd = &img[off];
21769           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
21770         }
21771         return cimg::type<double>::nan();
21772       }
21773 
21774       static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) {
21775         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21776         CImg<T> &img = mp.listout[ind];
21777         const int
21778           x = (int)_mp_arg(3),
21779           y = (int)_mp_arg(4),
21780           z = (int)_mp_arg(5);
21781         const T val = (T)_mp_arg(1);
21782         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
21783           T *ptrd = &img(x,y,z);
21784           const ulongT whd = (ulongT)img._width*img._height*img._depth;
21785           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
21786         }
21787         return _mp_arg(1);
21788       }
21789 
21790       static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) {
21791         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21792         CImg<T> &img = mp.listout[ind];
21793         const int
21794           x = (int)_mp_arg(3),
21795           y = (int)_mp_arg(4),
21796           z = (int)_mp_arg(5);
21797         const double *ptrs = &_mp_arg(1) + 1;
21798         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
21799           const unsigned int vsiz = (unsigned int)mp.opcode[6];
21800           T *ptrd = &img(x,y,z);
21801           const ulongT whd = (ulongT)img._width*img._height*img._depth;
21802           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
21803         }
21804         return cimg::type<double>::nan();
21805       }
21806 
21807       static double mp_list_set_Joff_s(_cimg_math_parser& mp) {
21808         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21809         CImg<T> &img = mp.listout[ind];
21810         const int
21811           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21812           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21813         const longT
21814           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
21815           whd = (longT)img.width()*img.height()*img.depth();
21816         const T val = (T)_mp_arg(1);
21817         if (off>=0 && off<whd) {
21818           T *ptrd = &img[off];
21819           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
21820         }
21821         return _mp_arg(1);
21822       }
21823 
21824       static double mp_list_set_Joff_v(_cimg_math_parser& mp) {
21825         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21826         CImg<T> &img = mp.listout[ind];
21827         const int
21828           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
21829           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
21830         const longT
21831           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
21832           whd = (longT)img.width()*img.height()*img.depth();
21833         const double *ptrs = &_mp_arg(1) + 1;
21834         if (off>=0 && off<whd) {
21835           const unsigned int vsiz = (unsigned int)mp.opcode[4];
21836           T *ptrd = &img[off];
21837           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
21838         }
21839         return cimg::type<double>::nan();
21840       }
21841 
21842       static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) {
21843         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21844         CImg<T> &img = mp.listout[ind];
21845         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
21846         const int
21847           x = (int)(ox + _mp_arg(3)),
21848           y = (int)(oy + _mp_arg(4)),
21849           z = (int)(oz + _mp_arg(5));
21850         const T val = (T)_mp_arg(1);
21851         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
21852           T *ptrd = &img(x,y,z);
21853           const ulongT whd = (ulongT)img._width*img._height*img._depth;
21854           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
21855         }
21856         return _mp_arg(1);
21857       }
21858 
21859       static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) {
21860         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21861         CImg<T> &img = mp.listout[ind];
21862         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
21863         const int
21864           x = (int)(ox + _mp_arg(3)),
21865           y = (int)(oy + _mp_arg(4)),
21866           z = (int)(oz + _mp_arg(5));
21867         const double *ptrs = &_mp_arg(1) + 1;
21868         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
21869           const unsigned int vsiz = (unsigned int)mp.opcode[6];
21870           T *ptrd = &img(x,y,z);
21871           const ulongT whd = (ulongT)img._width*img._height*img._depth;
21872           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
21873         }
21874         return cimg::type<double>::nan();
21875       }
21876 
21877       static double mp_list_spectrum(_cimg_math_parser& mp) {
21878         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21879         return (double)mp.listin[ind]._spectrum;
21880       }
21881 
21882       static double mp_list_stats(_cimg_math_parser& mp) {
21883         const unsigned int
21884           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21885           k = (unsigned int)mp.opcode[3];
21886         if (!mp.list_stats) mp.list_stats.assign(mp.listin._width);
21887         if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false);
21888         return mp.list_stats(ind,k);
21889       }
21890 
21891       static double mp_list_wh(_cimg_math_parser& mp) {
21892         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21893         return (double)mp.listin[ind]._width*mp.listin[ind]._height;
21894       }
21895 
21896       static double mp_list_whd(_cimg_math_parser& mp) {
21897         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21898         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth;
21899       }
21900 
21901       static double mp_list_whds(_cimg_math_parser& mp) {
21902         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21903         return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum;
21904       }
21905 
21906       static double mp_list_width(_cimg_math_parser& mp) {
21907         const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width());
21908         return (double)mp.listin[ind]._width;
21909       }
21910 
21911       static double mp_list_Ioff(_cimg_math_parser& mp) {
21912         double *ptrd = &_mp_arg(1) + 1;
21913         const unsigned int
21914           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21915           boundary_conditions = (unsigned int)_mp_arg(4),
21916           vsiz = (unsigned int)mp.opcode[5];
21917         const CImg<T> &img = mp.listin[ind];
21918         const longT
21919           off = (longT)_mp_arg(3),
21920           whd = (longT)img.width()*img.height()*img.depth();
21921         const T *ptrs;
21922         if (off>=0 && off<whd) {
21923           ptrs = &img[off];
21924           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21925           return cimg::type<double>::nan();
21926         }
21927         if (img._data) switch (boundary_conditions) {
21928           case 3 : { // Mirror
21929             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
21930             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
21931             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21932             return cimg::type<double>::nan();
21933           }
21934           case 2 : // Periodic
21935             ptrs = &img[cimg::mod(off,whd)];
21936             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21937             return cimg::type<double>::nan();
21938           case 1 : // Neumann
21939             ptrs = off<0?&img[0]:&img[whd - 1];
21940             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
21941             return cimg::type<double>::nan();
21942           default : // Dirichlet
21943             std::memset(ptrd,0,vsiz*sizeof(double));
21944             return cimg::type<double>::nan();
21945           }
21946         std::memset(ptrd,0,vsiz*sizeof(double));
21947         return cimg::type<double>::nan();
21948       }
21949 
21950       static double mp_list_Ixyz(_cimg_math_parser& mp) {
21951         double *ptrd = &_mp_arg(1) + 1;
21952         const unsigned int
21953           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
21954           interpolation = (unsigned int)_mp_arg(6),
21955           boundary_conditions = (unsigned int)_mp_arg(7),
21956           vsiz = (unsigned int)mp.opcode[8];
21957         const CImg<T> &img = mp.listin[ind];
21958         const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5);
21959         const ulongT whd = (ulongT)img._width*img._height*img._depth;
21960         const T *ptrs;
21961         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
21962           case 3 : { // Mirror
21963             const int
21964               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
21965               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
21966               cx = mx<img.width()?mx:w2 - mx - 1,
21967               cy = my<img.height()?my:h2 - my - 1,
21968               cz = mz<img.depth()?mz:d2 - mz - 1;
21969             ptrs = &img(cx,cy,cz);
21970             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21971           } break;
21972           case 2 : { // Periodic
21973             const int
21974               cx = cimg::mod((int)x,img.width()),
21975               cy = cimg::mod((int)y,img.height()),
21976               cz = cimg::mod((int)z,img.depth());
21977             ptrs = &img(cx,cy,cz);
21978             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21979           } break;
21980           case 1 : { // Neumann
21981             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
21982             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21983           } break;
21984           default : // Dirichlet
21985             if (img.containsXYZC((int)x,(int)y,(int)z)) {
21986               ptrs = &img((int)x,(int)y,(int)z);
21987               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
21988             } else std::memset(ptrd,0,vsiz*sizeof(double));
21989           } else switch (boundary_conditions) { // Linear interpolation
21990           case 3 : { // Mirror
21991             const float
21992               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
21993               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
21994               cx = mx<img.width()?mx:w2 - mx - 1,
21995               cy = my<img.height()?my:h2 - my - 1,
21996               cz = mz<img.depth()?mz:d2 - mz - 1;
21997             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
21998           } break;
21999           case 2 : { // Periodic
22000             const float
22001               cx = cimg::mod((float)x,(float)img.width()),
22002               cy = cimg::mod((float)y,(float)img.height()),
22003               cz = cimg::mod((float)z,(float)img.depth());
22004             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
22005           } break;
22006           case 1 : // Neumann
22007             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
22008             break;
22009           case 0 : // Dirichlet
22010             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
22011           }
22012         return cimg::type<double>::nan();
22013       }
22014 
22015       static double mp_list_Joff(_cimg_math_parser& mp) {
22016         double *ptrd = &_mp_arg(1) + 1;
22017         const unsigned int
22018           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
22019           boundary_conditions = (unsigned int)_mp_arg(4),
22020           vsiz = (unsigned int)mp.opcode[5];
22021         const int
22022           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z];
22023         const CImg<T> &img = mp.listin[ind];
22024         const longT
22025           off = img.offset(ox,oy,oz) + (longT)_mp_arg(3),
22026           whd = (longT)img.width()*img.height()*img.depth();
22027         const T *ptrs;
22028         if (off>=0 && off<whd) {
22029           ptrs = &img[off];
22030           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
22031           return cimg::type<double>::nan();
22032         }
22033         if (img._data) switch (boundary_conditions) {
22034           case 3 : { // Mirror
22035             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
22036             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
22037             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
22038             return cimg::type<double>::nan();
22039           }
22040           case 2 : // Periodic
22041             ptrs = &img[cimg::mod(off,whd)];
22042             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
22043             return cimg::type<double>::nan();
22044           case 1 : // Neumann
22045             ptrs = off<0?&img[0]:&img[whd - 1];
22046             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
22047             return cimg::type<double>::nan();
22048           default : // Dirichlet
22049             std::memset(ptrd,0,vsiz*sizeof(double));
22050             return cimg::type<double>::nan();
22051           }
22052         std::memset(ptrd,0,vsiz*sizeof(double));
22053         return cimg::type<double>::nan();
22054       }
22055 
22056       static double mp_list_Jxyz(_cimg_math_parser& mp) {
22057         double *ptrd = &_mp_arg(1) + 1;
22058         const unsigned int
22059           ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()),
22060           interpolation = (unsigned int)_mp_arg(6),
22061           boundary_conditions = (unsigned int)_mp_arg(7),
22062           vsiz = (unsigned int)mp.opcode[8];
22063         const CImg<T> &img = mp.listin[ind];
22064         const double
22065           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
22066           x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5);
22067         const ulongT whd = (ulongT)img._width*img._height*img._depth;
22068         const T *ptrs;
22069         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
22070           case 3 : { // Mirror
22071             const int
22072               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
22073               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
22074               cx = mx<img.width()?mx:w2 - mx - 1,
22075               cy = my<img.height()?my:h2 - my - 1,
22076               cz = mz<img.depth()?mz:d2 - mz - 1;
22077             ptrs = &img(cx,cy,cz);
22078             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
22079           } break;
22080           case 2 : { // Periodic
22081             const int
22082               cx = cimg::mod((int)x,img.width()),
22083               cy = cimg::mod((int)y,img.height()),
22084               cz = cimg::mod((int)z,img.depth());
22085             ptrs = &img(cx,cy,cz);
22086             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
22087           } break;
22088           case 1 : { // Neumann
22089             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
22090             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
22091           } break;
22092           default : // Dirichlet
22093             if (img.containsXYZC((int)x,(int)y,(int)z)) {
22094               ptrs = &img((int)x,(int)y,(int)z);
22095               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
22096             } else std::memset(ptrd,0,vsiz*sizeof(double));
22097           } else switch (boundary_conditions) { // Linear interpolation
22098           case 3 : { // Mirror
22099             const float
22100               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
22101               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
22102               cx = mx<img.width()?mx:w2 - mx - 1,
22103               cy = my<img.height()?my:h2 - my - 1,
22104               cz = mz<img.depth()?mz:d2 - mz - 1;
22105             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
22106           } break;
22107           case 2 : { // Periodic
22108             const float
22109               cx = cimg::mod((float)x,(float)img.width()),
22110               cy = cimg::mod((float)y,(float)img.height()),
22111               cz = cimg::mod((float)z,(float)img.depth());
22112             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
22113           } break;
22114           case 1 : // Neumann
22115             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
22116             break;
22117           default : // Dirichlet
22118             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
22119           }
22120         return cimg::type<double>::nan();
22121       }
22122 
22123       static double mp_log(_cimg_math_parser& mp) {
22124         return std::log(_mp_arg(2));
22125       }
22126 
22127       static double mp_log10(_cimg_math_parser& mp) {
22128         return std::log10(_mp_arg(2));
22129       }
22130 
22131       static double mp_log2(_cimg_math_parser& mp) {
22132         return cimg::log2(_mp_arg(2));
22133       }
22134 
22135       static double mp_logical_and(_cimg_math_parser& mp) {
22136         const bool val_left = (bool)_mp_arg(2);
22137         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
22138         if (!val_left) { mp.p_code = p_end - 1; return 0; }
22139         const ulongT mem_right = mp.opcode[3];
22140         for ( ; mp.p_code<p_end; ++mp.p_code) {
22141           mp.opcode._data = mp.p_code->_data;
22142           const ulongT target = mp.opcode[1];
22143           mp.mem[target] = _cimg_mp_defunc(mp);
22144         }
22145         --mp.p_code;
22146         return (double)(bool)mp.mem[mem_right];
22147       }
22148 
22149       static double mp_logical_not(_cimg_math_parser& mp) {
22150         return (double)!_mp_arg(2);
22151       }
22152 
22153       static double mp_logical_or(_cimg_math_parser& mp) {
22154         const bool val_left = (bool)_mp_arg(2);
22155         const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
22156         if (val_left) { mp.p_code = p_end - 1; return 1; }
22157         const ulongT mem_right = mp.opcode[3];
22158         for ( ; mp.p_code<p_end; ++mp.p_code) {
22159           mp.opcode._data = mp.p_code->_data;
22160           const ulongT target = mp.opcode[1];
22161           mp.mem[target] = _cimg_mp_defunc(mp);
22162         }
22163         --mp.p_code;
22164         return (double)(bool)mp.mem[mem_right];
22165       }
22166 
22167       static double mp_lowercase(_cimg_math_parser& mp) {
22168         return cimg::lowercase(_mp_arg(2));
22169       }
22170 
22171       static double mp_lt(_cimg_math_parser& mp) {
22172         return (double)(_mp_arg(2)<_mp_arg(3));
22173       }
22174 
22175       static double mp_lte(_cimg_math_parser& mp) {
22176         return (double)(_mp_arg(2)<=_mp_arg(3));
22177       }
22178 
22179       static double mp_matrix_eig(_cimg_math_parser& mp) {
22180         double *ptrd = &_mp_arg(1) + 1;
22181         const double *ptr1 = &_mp_arg(2) + 1;
22182         const unsigned int k = (unsigned int)mp.opcode[3];
22183         CImg<doubleT> val, vec;
22184         CImg<doubleT>(ptr1,k,k,1,1,true).symmetric_eigen(val,vec);
22185         CImg<doubleT>(ptrd,1,k,1,1,true) = val;
22186         CImg<doubleT>(ptrd + k,k,k,1,1,true) = vec.get_transpose();
22187         return cimg::type<double>::nan();
22188       }
22189 
22190       static double mp_matrix_inv(_cimg_math_parser& mp) {
22191         double *ptrd = &_mp_arg(1) + 1;
22192         const double *ptr1 = &_mp_arg(2) + 1;
22193         const unsigned int k = (unsigned int)mp.opcode[3];
22194         CImg<doubleT>(ptrd,k,k,1,1,true) = CImg<doubleT>(ptr1,k,k,1,1,true).get_invert();
22195         return cimg::type<double>::nan();
22196       }
22197 
22198       static double mp_matrix_mul(_cimg_math_parser& mp) {
22199         double *ptrd = &_mp_arg(1) + 1;
22200         const double
22201           *ptr1 = &_mp_arg(2) + 1,
22202           *ptr2 = &_mp_arg(3) + 1;
22203         const unsigned int
22204           k = (unsigned int)mp.opcode[4],
22205           l = (unsigned int)mp.opcode[5],
22206           m = (unsigned int)mp.opcode[6];
22207         CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr1,l,k,1,1,true)*CImg<doubleT>(ptr2,m,l,1,1,true);
22208         return cimg::type<double>::nan();
22209       }
22210 
22211       static double mp_matrix_pseudoinv(_cimg_math_parser& mp) {
22212         double *ptrd = &_mp_arg(1) + 1;
22213         const double *ptr1 = &_mp_arg(2) + 1;
22214         const unsigned int
22215           k = (unsigned int)mp.opcode[3],
22216           l = (unsigned int)mp.opcode[4];
22217         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptr1,k,l,1,1,true).get_pseudoinvert();
22218         return cimg::type<double>::nan();
22219       }
22220 
22221       static double mp_matrix_svd(_cimg_math_parser& mp) {
22222         double *ptrd = &_mp_arg(1) + 1;
22223         const double *ptr1 = &_mp_arg(2) + 1;
22224         const unsigned int
22225           k = (unsigned int)mp.opcode[3],
22226           l = (unsigned int)mp.opcode[4];
22227         CImg<doubleT> U, S, V;
22228         CImg<doubleT>(ptr1,k,l,1,1,true).SVD(U,S,V);
22229         CImg<doubleT>(ptrd,k,l,1,1,true) = U;
22230         CImg<doubleT>(ptrd + k*l,1,k,1,1,true) = S;
22231         CImg<doubleT>(ptrd + k*l + k,k,k,1,1,true) = V;
22232         return cimg::type<double>::nan();
22233       }
22234 
22235       static double mp_max(_cimg_math_parser& mp) {
22236         const unsigned int i_end = (unsigned int)mp.opcode[2];
22237         double val = _mp_arg(3);
22238         for (unsigned int i = 4; i<i_end; ++i) val = std::max(val,_mp_arg(i));
22239         return val;
22240       }
22241 
22242       static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref,
22243                                         const longT siz, const long inc) {
22244         const longT
22245           off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind,
22246           eoff = off + (siz - 1)*inc;
22247         if (off<0 || eoff>=mp.mem.width())
22248           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
22249                                       "Out-of-bounds variable pointer "
22250                                       "(length: %ld, increment: %ld, offset start: %ld, "
22251                                       "offset end: %ld, offset max: %u).",
22252                                       mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1);
22253         return &mp.mem[off];
22254       }
22255 
22256       static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref,
22257                                       const longT siz, const long inc) {
22258         const unsigned ind = (unsigned int)p_ref[1];
22259         const CImg<T> &img = ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())];
22260         const bool is_relative = (bool)p_ref[2];
22261         int ox, oy, oz, oc;
22262         longT off = 0;
22263         if (is_relative) {
22264           ox = (int)mp.mem[_cimg_mp_slot_x];
22265           oy = (int)mp.mem[_cimg_mp_slot_y];
22266           oz = (int)mp.mem[_cimg_mp_slot_z];
22267           oc = (int)mp.mem[_cimg_mp_slot_c];
22268           off = img.offset(ox,oy,oz,oc);
22269         }
22270         if ((*p_ref)%2) {
22271           const int
22272             x = (int)mp.mem[p_ref[3]],
22273             y = (int)mp.mem[p_ref[4]],
22274             z = (int)mp.mem[p_ref[5]],
22275             c = *p_ref==5?0:(int)mp.mem[p_ref[6]];
22276           off+=img.offset(x,y,z,c);
22277         } else off+=(longT)mp.mem[p_ref[3]];
22278         const longT eoff = off + (siz - 1)*inc;
22279         if (off<0 || eoff>=(longT)img.size())
22280           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
22281                                       "Out-of-bounds image pointer "
22282                                       "(length: %ld, increment: %ld, offset start: %ld, "
22283                                       "offset end: %ld, offset max: %lu).",
22284                                       mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1);
22285         return (float*)&img[off];
22286       }
22287 
22288       static double mp_memcopy(_cimg_math_parser& mp) {
22289         longT siz = (longT)_mp_arg(4);
22290         const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6);
22291         const float
22292           _opacity = (float)_mp_arg(7),
22293           opacity = (float)cimg::abs(_opacity),
22294           omopacity = 1 - std::max(_opacity,0.0f);
22295         if (siz>0) {
22296           const bool
22297             is_doubled = mp.opcode[8]<=1,
22298             is_doubles = mp.opcode[15]<=1;
22299           if (is_doubled && is_doubles) { // (double*) <- (double*)
22300             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
22301             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
22302             if (inc_d==1 && inc_s==1 && _opacity>=1) {
22303               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double));
22304               else std::memmove(ptrd,ptrs,siz*sizeof(double));
22305             } else {
22306               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
22307                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22308                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22309               } else { // Overlapping buffers
22310                 CImg<doubleT> buf((unsigned int)siz);
22311                 cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; }
22312                 ptrs = buf;
22313                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
22314                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
22315               }
22316             }
22317           } else if (is_doubled && !is_doubles) { // (double*) <- (float*)
22318             double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
22319             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s);
22320             if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22321             else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22322           } else if (!is_doubled && is_doubles) { // (float*) <- (double*)
22323             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d);
22324             const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
22325             if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22326             else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; }
22327           } else { // (float*) <- (float*)
22328             float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d);
22329             const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s);
22330             if (inc_d==1 && inc_s==1 && _opacity>=1) {
22331               if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float));
22332               else std::memmove(ptrd,ptrs,siz*sizeof(float));
22333             } else {
22334               if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
22335                 if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22336                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
22337               } else { // Overlapping buffers
22338                 CImg<floatT> buf((unsigned int)siz);
22339                 cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; }
22340                 ptrs = buf;
22341                 if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
22342                 else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
22343               }
22344             }
22345           }
22346         }
22347         return _mp_arg(1);
22348       }
22349 
22350       static double mp_min(_cimg_math_parser& mp) {
22351         const unsigned int i_end = (unsigned int)mp.opcode[2];
22352         double val = _mp_arg(3);
22353         for (unsigned int i = 4; i<i_end; ++i) val = std::min(val,_mp_arg(i));
22354         return val;
22355       }
22356 
22357       static double mp_minus(_cimg_math_parser& mp) {
22358         return -_mp_arg(2);
22359       }
22360 
22361       static double mp_median(_cimg_math_parser& mp) {
22362         const unsigned int i_end = (unsigned int)mp.opcode[2];
22363         switch (i_end - 3) {
22364         case 1 : return _mp_arg(3);
22365         case 2 : return cimg::median(_mp_arg(3),_mp_arg(4));
22366         case 3 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5));
22367         case 5 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7));
22368         case 7 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9));
22369         case 9 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9),
22370                                      _mp_arg(10),_mp_arg(11));
22371         case 13 : return cimg::median(_mp_arg(3),_mp_arg(4),_mp_arg(5),_mp_arg(6),_mp_arg(7),_mp_arg(8),_mp_arg(9),
22372                                       _mp_arg(10),_mp_arg(11),_mp_arg(12),_mp_arg(13),_mp_arg(14),_mp_arg(15));
22373         }
22374         CImg<doubleT> vals(i_end - 3);
22375         double *p = vals.data();
22376         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
22377         return vals.median();
22378       }
22379 
22380       static double mp_modulo(_cimg_math_parser& mp) {
22381         return cimg::mod(_mp_arg(2),_mp_arg(3));
22382       }
22383 
22384       static double mp_mul(_cimg_math_parser& mp) {
22385         return _mp_arg(2)*_mp_arg(3);
22386       }
22387 
22388       static double mp_mul2(_cimg_math_parser& mp) {
22389         return _mp_arg(2)*_mp_arg(3)*_mp_arg(4);
22390       }
22391 
22392       static double mp_neq(_cimg_math_parser& mp) {
22393         return (double)(_mp_arg(2)!=_mp_arg(3));
22394       }
22395 
22396       static double mp_norm0(_cimg_math_parser& mp) {
22397         const unsigned int i_end = (unsigned int)mp.opcode[2];
22398         switch (i_end - 3) {
22399         case 1 : return _mp_arg(3)!=0;
22400         case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0);
22401         }
22402         double res = 0;
22403         for (unsigned int i = 3; i<i_end; ++i)
22404           res+=_mp_arg(i)==0?0:1;
22405         return res;
22406       }
22407 
22408       static double mp_norm1(_cimg_math_parser& mp) {
22409         const unsigned int i_end = (unsigned int)mp.opcode[2];
22410         switch (i_end - 3) {
22411         case 1 : return cimg::abs(_mp_arg(3));
22412         case 2 : return cimg::abs(_mp_arg(3)) + cimg::abs(_mp_arg(4));
22413         }
22414         double res = 0;
22415         for (unsigned int i = 3; i<i_end; ++i)
22416           res+=cimg::abs(_mp_arg(i));
22417         return res;
22418       }
22419 
22420       static double mp_norm2(_cimg_math_parser& mp) {
22421         const unsigned int i_end = (unsigned int)mp.opcode[2];
22422         switch (i_end - 3) {
22423         case 1 : return cimg::abs(_mp_arg(3));
22424         case 2 : return cimg::_hypot(_mp_arg(3),_mp_arg(4));
22425         }
22426         double res = 0;
22427         for (unsigned int i = 3; i<i_end; ++i)
22428           res+=cimg::sqr(_mp_arg(i));
22429         return std::sqrt(res);
22430       }
22431 
22432       static double mp_norminf(_cimg_math_parser& mp) {
22433         const unsigned int i_end = (unsigned int)mp.opcode[2];
22434         switch (i_end - 3) {
22435         case 1 : return cimg::abs(_mp_arg(3));
22436         case 2 : return std::max(cimg::abs(_mp_arg(3)),cimg::abs(_mp_arg(4)));
22437         }
22438         double res = 0;
22439         for (unsigned int i = 3; i<i_end; ++i) {
22440           const double val = cimg::abs(_mp_arg(i));
22441           if (val>res) res = val;
22442         }
22443         return res;
22444       }
22445 
22446       static double mp_normp(_cimg_math_parser& mp) {
22447         const unsigned int i_end = (unsigned int)mp.opcode[2];
22448         if (i_end==4) return cimg::abs(_mp_arg(3));
22449         const double p = (double)mp.opcode[3];
22450         double res = 0;
22451         for (unsigned int i = 4; i<i_end; ++i)
22452           res+=std::pow(cimg::abs(_mp_arg(i)),p);
22453         res = std::pow(res,1/p);
22454         return res>0?res:0.0;
22455       }
22456 
22457       static double mp_permutations(_cimg_math_parser& mp) {
22458         return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4));
22459       }
22460 
22461       static double mp_polygon(_cimg_math_parser& mp) {
22462         const unsigned int i_end = (unsigned int)mp.opcode[2];
22463         unsigned int ind = (unsigned int)mp.opcode[3];
22464         if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width());
22465         CImg<T> &img = ind==~0U?mp.imgout:mp.listout[ind];
22466         bool is_invalid_arguments = i_end<=4;
22467         if (!is_invalid_arguments) {
22468           const int nbv = (int)_mp_arg(4);
22469           if (nbv<=0) is_invalid_arguments = true;
22470           else {
22471             CImg<intT> points(nbv,2,1,1,0);
22472             CImg<T> color(img._spectrum,1,1,1,0);
22473             float opacity = 1;
22474             unsigned int i = 5;
22475             cimg_foroff(points,k) if (i<i_end) points(k/2,k%2) = (int)cimg::round(_mp_arg(i++));
22476             else { is_invalid_arguments = true; break; }
22477             if (!is_invalid_arguments) {
22478               if (i<i_end) opacity = (float)_mp_arg(i++);
22479               cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
22480               else { color.resize(k,1,1,1,-1); break; }
22481               color.resize(img._spectrum,1,1,1,0,2);
22482               img.draw_polygon(points,color._data,opacity);
22483             }
22484           }
22485         }
22486         if (is_invalid_arguments) {
22487           CImg<doubleT> args(i_end - 4);
22488           cimg_forX(args,k) args[k] = _mp_arg(4 + k);
22489           if (ind==~0U)
22490             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
22491                                         "Invalid arguments '%s'. ",
22492                                         mp.imgin.pixel_type(),args.value_string()._data);
22493           else
22494             throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
22495                                         "Invalid arguments '#%u%s%s'. ",
22496                                         mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
22497         }
22498         return cimg::type<double>::nan();
22499       }
22500 
22501       static double mp_pow(_cimg_math_parser& mp) {
22502         const double v = _mp_arg(2), p = _mp_arg(3);
22503         return std::pow(v,p);
22504       }
22505 
22506       static double mp_pow0_25(_cimg_math_parser& mp) {
22507         const double val = _mp_arg(2);
22508         return std::sqrt(std::sqrt(val));
22509       }
22510 
22511       static double mp_pow3(_cimg_math_parser& mp) {
22512         const double val = _mp_arg(2);
22513         return val*val*val;
22514       }
22515 
22516       static double mp_pow4(_cimg_math_parser& mp) {
22517         const double val = _mp_arg(2);
22518         return val*val*val*val;
22519       }
22520 
22521       static double mp_print(_cimg_math_parser& mp) {
22522           const double val = _mp_arg(1);
22523           const bool print_char = (bool)mp.opcode[3];
22524           cimg_pragma_openmp(critical(mp_print))
22525           {
22526             CImg<charT> expr(mp.opcode[2] - 4);
22527             const ulongT *ptrs = mp.opcode._data + 4;
22528             cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
22529             cimg::strellipsize(expr);
22530             cimg::mutex(6);
22531             if (print_char)
22532               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g = '%c'",expr._data,val,(int)val);
22533             else
22534               std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %g",expr._data,val);
22535             std::fflush(cimg::output());
22536             cimg::mutex(6,0);
22537           }
22538           return val;
22539       }
22540 
22541       static double mp_prod(_cimg_math_parser& mp) {
22542         const unsigned int i_end = (unsigned int)mp.opcode[2];
22543         double val = _mp_arg(3);
22544         for (unsigned int i = 4; i<i_end; ++i) val*=_mp_arg(i);
22545         return val;
22546       }
22547 
22548       static double mp_copy(_cimg_math_parser& mp) {
22549         return _mp_arg(2);
22550       }
22551 
22552       static double mp_rol(_cimg_math_parser& mp) {
22553         return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3));
22554       }
22555 
22556       static double mp_ror(_cimg_math_parser& mp) {
22557         return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3));
22558       }
22559 
22560       static double mp_rot2d(_cimg_math_parser& mp) {
22561         double *ptrd = &_mp_arg(1) + 1;
22562         const float
22563           theta = (float)_mp_arg(2)*cimg::PI/180,
22564           ca = std::cos(theta),
22565           sa = std::sin(theta);
22566         *(ptrd++) = ca;
22567         *(ptrd++) = -sa;
22568         *(ptrd++) = sa;
22569         *ptrd = ca;
22570         return cimg::type<double>::nan();
22571       }
22572 
22573       static double mp_rot3d(_cimg_math_parser& mp) {
22574         double *ptrd = &_mp_arg(1) + 1;
22575         const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5);
22576         CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta);
22577         return cimg::type<double>::nan();
22578       }
22579 
22580       static double mp_round(_cimg_math_parser& mp) {
22581         return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));
22582       }
22583 
22584       static double mp_self_add(_cimg_math_parser& mp) {
22585         return _mp_arg(1)+=_mp_arg(2);
22586       }
22587 
22588       static double mp_self_bitwise_and(_cimg_math_parser& mp) {
22589         double &val = _mp_arg(1);
22590         return val = (double)((longT)val & (longT)_mp_arg(2));
22591       }
22592 
22593       static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) {
22594         double &val = _mp_arg(1);
22595         return val = (double)((longT)val<<(unsigned int)_mp_arg(2));
22596       }
22597 
22598       static double mp_self_bitwise_or(_cimg_math_parser& mp) {
22599         double &val = _mp_arg(1);
22600         return val = (double)((longT)val | (longT)_mp_arg(2));
22601       }
22602 
22603       static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) {
22604         double &val = _mp_arg(1);
22605         return val = (double)((longT)val>>(unsigned int)_mp_arg(2));
22606       }
22607 
22608       static double mp_self_decrement(_cimg_math_parser& mp) {
22609         return --_mp_arg(1);
22610       }
22611 
22612       static double mp_self_increment(_cimg_math_parser& mp) {
22613         return ++_mp_arg(1);
22614       }
22615 
22616       static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar
22617         unsigned int
22618           ptrd = (unsigned int)mp.opcode[1] + 1,
22619           siz = (unsigned int)mp.opcode[2];
22620         mp_func op = (mp_func)mp.opcode[3];
22621         CImg<ulongT> l_opcode(1,3);
22622         l_opcode[2] = mp.opcode[4]; // Scalar argument.
22623         l_opcode.swap(mp.opcode);
22624         ulongT &target = mp.opcode[1];
22625         while (siz-->0) { target = ptrd++; (*op)(mp); }
22626         l_opcode.swap(mp.opcode);
22627         return cimg::type<double>::nan();
22628       }
22629 
22630       static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector
22631         unsigned int
22632           ptrd = (unsigned int)mp.opcode[1] + 1,
22633           siz = (unsigned int)mp.opcode[2],
22634           ptrs = (unsigned int)mp.opcode[4] + 1;
22635         mp_func op = (mp_func)mp.opcode[3];
22636         CImg<ulongT> l_opcode(1,4);
22637         l_opcode.swap(mp.opcode);
22638         ulongT &target = mp.opcode[1], &argument = mp.opcode[2];
22639         while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); }
22640         l_opcode.swap(mp.opcode);
22641         return cimg::type<double>::nan();
22642       }
22643 
22644       static double mp_self_mul(_cimg_math_parser& mp) {
22645         return _mp_arg(1)*=_mp_arg(2);
22646       }
22647 
22648       static double mp_self_div(_cimg_math_parser& mp) {
22649         return _mp_arg(1)/=_mp_arg(2);
22650       }
22651 
22652       static double mp_self_modulo(_cimg_math_parser& mp) {
22653         double &val = _mp_arg(1);
22654         return val = cimg::mod(val,_mp_arg(2));
22655       }
22656 
22657       static double mp_self_pow(_cimg_math_parser& mp) {
22658         double &val = _mp_arg(1);
22659         return val = std::pow(val,_mp_arg(2));
22660       }
22661 
22662       static double mp_self_sub(_cimg_math_parser& mp) {
22663         return _mp_arg(1)-=_mp_arg(2);
22664       }
22665 
22666       static double mp_set_ioff(_cimg_math_parser& mp) {
22667         CImg<T> &img = mp.imgout;
22668         const longT
22669           off = (longT)_mp_arg(2),
22670           whds = (longT)img.size();
22671         const double val = _mp_arg(1);
22672         if (off>=0 && off<whds) img[off] = (T)val;
22673         return val;
22674       }
22675 
22676       static double mp_set_ixyzc(_cimg_math_parser& mp) {
22677         CImg<T> &img = mp.imgout;
22678         const int
22679           x = (int)_mp_arg(2), y = (int)_mp_arg(3),
22680           z = (int)_mp_arg(4), c = (int)_mp_arg(5);
22681         const double val = _mp_arg(1);
22682         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
22683             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
22684           img(x,y,z,c) = (T)val;
22685         return val;
22686       }
22687 
22688       static double mp_set_joff(_cimg_math_parser& mp) {
22689         CImg<T> &img = mp.imgout;
22690         const int
22691           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
22692           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
22693         const longT
22694           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
22695           whds = (longT)img.size();
22696         const double val = _mp_arg(1);
22697         if (off>=0 && off<whds) img[off] = (T)val;
22698         return val;
22699       }
22700 
22701       static double mp_set_jxyzc(_cimg_math_parser& mp) {
22702         CImg<T> &img = mp.imgout;
22703         const double
22704           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
22705           oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
22706         const int
22707           x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)),
22708           z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5));
22709         const double val = _mp_arg(1);
22710         if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
22711             z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
22712           img(x,y,z,c) = (T)val;
22713         return val;
22714       }
22715 
22716       static double mp_set_Ioff_s(_cimg_math_parser& mp) {
22717         CImg<T> &img = mp.imgout;
22718         const longT
22719           off = (longT)_mp_arg(2),
22720           whd = (longT)img.width()*img.height()*img.depth();
22721         const T val = (T)_mp_arg(1);
22722         if (off>=0 && off<whd) {
22723           T *ptrd = &img[off];
22724           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
22725         }
22726         return _mp_arg(1);
22727       }
22728 
22729       static double mp_set_Ioff_v(_cimg_math_parser& mp) {
22730         CImg<T> &img = mp.imgout;
22731         const longT
22732           off = (longT)_mp_arg(2),
22733           whd = (longT)img.width()*img.height()*img.depth();
22734         const double *ptrs = &_mp_arg(1) + 1;
22735         if (off>=0 && off<whd) {
22736           const unsigned int vsiz = (unsigned int)mp.opcode[3];
22737           T *ptrd = &img[off];
22738           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
22739         }
22740         return cimg::type<double>::nan();
22741       }
22742 
22743       static double mp_set_Ixyz_s(_cimg_math_parser& mp) {
22744         CImg<T> &img = mp.imgout;
22745         const int
22746           x = (int)_mp_arg(2),
22747           y = (int)_mp_arg(3),
22748           z = (int)_mp_arg(4);
22749         const T val = (T)_mp_arg(1);
22750         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
22751           T *ptrd = &img(x,y,z);
22752           const ulongT whd = (ulongT)img._width*img._height*img._depth;
22753           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
22754         }
22755         return _mp_arg(1);
22756       }
22757 
22758       static double mp_set_Ixyz_v(_cimg_math_parser& mp) {
22759         CImg<T> &img = mp.imgout;
22760         const int
22761           x = (int)_mp_arg(2),
22762           y = (int)_mp_arg(3),
22763           z = (int)_mp_arg(4);
22764         const double *ptrs = &_mp_arg(1) + 1;
22765         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
22766           const unsigned int vsiz = (unsigned int)mp.opcode[5];
22767           T *ptrd = &img(x,y,z);
22768           const ulongT whd = (ulongT)img._width*img._height*img._depth;
22769           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
22770         }
22771         return cimg::type<double>::nan();
22772       }
22773 
22774       static double mp_set_Joff_s(_cimg_math_parser& mp) {
22775         CImg<T> &img = mp.imgout;
22776         const int
22777           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
22778           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
22779         const longT
22780           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
22781           whd = (longT)img.width()*img.height()*img.depth();
22782         const T val = (T)_mp_arg(1);
22783         if (off>=0 && off<whd) {
22784           T *ptrd = &img[off];
22785           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
22786         }
22787         return _mp_arg(1);
22788       }
22789 
22790       static double mp_set_Joff_v(_cimg_math_parser& mp) {
22791         CImg<T> &img = mp.imgout;
22792         const int
22793           ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
22794           oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
22795         const longT
22796           off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
22797           whd = (longT)img.width()*img.height()*img.depth();
22798         const double *ptrs = &_mp_arg(1) + 1;
22799         if (off>=0 && off<whd) {
22800           const unsigned int vsiz = (unsigned int)mp.opcode[3];
22801           T *ptrd = &img[off];
22802           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
22803         }
22804         return cimg::type<double>::nan();
22805       }
22806 
22807       static double mp_set_Jxyz_s(_cimg_math_parser& mp) {
22808         CImg<T> &img = mp.imgout;
22809         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
22810         const int
22811           x = (int)(ox + _mp_arg(2)),
22812           y = (int)(oy + _mp_arg(3)),
22813           z = (int)(oz + _mp_arg(4));
22814         const T val = (T)_mp_arg(1);
22815         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
22816           T *ptrd = &img(x,y,z);
22817           const ulongT whd = (ulongT)img._width*img._height*img._depth;
22818           cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
22819         }
22820         return _mp_arg(1);
22821       }
22822 
22823       static double mp_set_Jxyz_v(_cimg_math_parser& mp) {
22824         CImg<T> &img = mp.imgout;
22825         const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
22826         const int
22827           x = (int)(ox + _mp_arg(2)),
22828           y = (int)(oy + _mp_arg(3)),
22829           z = (int)(oz + _mp_arg(4));
22830         const double *ptrs = &_mp_arg(1) + 1;
22831         if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
22832           const unsigned int vsiz = (unsigned int)mp.opcode[5];
22833           T *ptrd = &img(x,y,z);
22834           const ulongT whd = (ulongT)img._width*img._height*img._depth;
22835           cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
22836         }
22837         return cimg::type<double>::nan();
22838       }
22839 
22840       static double mp_shift(_cimg_math_parser& mp) {
22841         double *const ptrd = &_mp_arg(1) + 1;
22842         const double *const ptrs = &_mp_arg(2) + 1;
22843         const unsigned int siz = (unsigned int)mp.opcode[3];
22844         const int
22845           shift = (int)_mp_arg(4),
22846           boundary_conditions = (int)_mp_arg(5);
22847         CImg<doubleT>(ptrd,siz,1,1,1,true) = CImg<doubleT>(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions);
22848         return cimg::type<double>::nan();
22849       }
22850 
22851       static double mp_sign(_cimg_math_parser& mp) {
22852         return cimg::sign(_mp_arg(2));
22853       }
22854 
22855       static double mp_sin(_cimg_math_parser& mp) {
22856         return std::sin(_mp_arg(2));
22857       }
22858 
22859       static double mp_sinc(_cimg_math_parser& mp) {
22860         return cimg::sinc(_mp_arg(2));
22861       }
22862 
22863       static double mp_sinh(_cimg_math_parser& mp) {
22864         return std::sinh(_mp_arg(2));
22865       }
22866 
22867       static double mp_solve(_cimg_math_parser& mp) {
22868         double *ptrd = &_mp_arg(1) + 1;
22869         const double
22870           *ptr1 = &_mp_arg(2) + 1,
22871           *ptr2 = &_mp_arg(3) + 1;
22872         const unsigned int
22873           k = (unsigned int)mp.opcode[4],
22874           l = (unsigned int)mp.opcode[5],
22875           m = (unsigned int)mp.opcode[6];
22876         CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr2,m,l,1,1,true).get_solve(CImg<doubleT>(ptr1,k,l,1,1,true));
22877         return cimg::type<double>::nan();
22878       }
22879 
22880       static double mp_sort(_cimg_math_parser& mp) {
22881         double *const ptrd = &_mp_arg(1) + 1;
22882         const double *const ptrs = &_mp_arg(2) + 1;
22883         const unsigned int
22884           siz = (unsigned int)mp.opcode[3],
22885           chunk_siz = (unsigned int)mp.opcode[5];
22886         const bool is_increasing = (bool)_mp_arg(4);
22887         CImg<doubleT>(ptrd,chunk_siz,siz/chunk_siz,1,1,true) = CImg<doubleT>(ptrs,chunk_siz,siz/chunk_siz,1,1,true).
22888           get_sort(is_increasing,chunk_siz>1?'y':0);
22889         return cimg::type<double>::nan();
22890       }
22891 
22892       static double mp_sqr(_cimg_math_parser& mp) {
22893         return cimg::sqr(_mp_arg(2));
22894       }
22895 
22896       static double mp_sqrt(_cimg_math_parser& mp) {
22897         return std::sqrt(_mp_arg(2));
22898       }
22899 
22900       static double mp_srand(_cimg_math_parser& mp) {
22901         return cimg::srand((unsigned int)_mp_arg(2));
22902       }
22903 
22904       static double mp_srand0(_cimg_math_parser& mp) {
22905         cimg::unused(mp);
22906         return cimg::srand();
22907       }
22908 
22909       static double mp_std(_cimg_math_parser& mp) {
22910         const unsigned int i_end = (unsigned int)mp.opcode[2];
22911         CImg<doubleT> vals(i_end - 3);
22912         double *p = vals.data();
22913         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
22914         return std::sqrt(vals.variance());
22915       }
22916 
22917       static double mp_string_init(_cimg_math_parser& mp) {
22918         const char *ptrs = (char*)&mp.opcode[3];
22919         unsigned int
22920           ptrd = (unsigned int)mp.opcode[1] + 1,
22921           siz = (unsigned int)mp.opcode[2];
22922         while (siz-->0) mp.mem[ptrd++] = (double)*(ptrs++);
22923         return cimg::type<double>::nan();
22924       }
22925 
22926       static double mp_stov(_cimg_math_parser& mp) {
22927         const double *ptrs = &_mp_arg(2);
22928         const ulongT siz = (ulongT)mp.opcode[3];
22929         longT ind = (longT)_mp_arg(4);
22930         const bool is_strict = (bool)_mp_arg(5);
22931         double val = cimg::type<double>::nan();
22932         if (ind<0 || ind>=(longT)siz) return val;
22933         if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val;
22934 
22935         CImg<charT> ss(siz + 1 - ind);
22936         char sep;
22937         ptrs+=1 + ind; cimg_forX(ss,i) ss[i] = (char)*(ptrs++); ss.back() = 0;
22938 
22939         int err = std::sscanf(ss,"%lf%c",&val,&sep);
22940 #if cimg_OS==2
22941         // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
22942         // to read those particular values.
22943         if (!err && (*ss=='+' || *ss=='-' || *ss=='i' || *ss=='I' || *ss=='n' || *ss=='N')) {
22944           bool is_positive = true;
22945           const char *s = ss;
22946           if (*s=='+') ++s; else if (*s=='-') { ++s; is_positive = false; }
22947           if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); err = 1; }
22948           else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); err = 1; }
22949           if (err==1 && !is_positive) val = -val;
22950         }
22951 #endif
22952         if (is_strict && err!=1) return cimg::type<double>::nan();
22953         return val;
22954       }
22955 
22956       static double mp_sub(_cimg_math_parser& mp) {
22957         return _mp_arg(2) - _mp_arg(3);
22958       }
22959 
22960       static double mp_sum(_cimg_math_parser& mp) {
22961         const unsigned int i_end = (unsigned int)mp.opcode[2];
22962         double val = _mp_arg(3);
22963         for (unsigned int i = 4; i<i_end; ++i) val+=_mp_arg(i);
22964         return val;
22965       }
22966 
22967       static double mp_tan(_cimg_math_parser& mp) {
22968         return std::tan(_mp_arg(2));
22969       }
22970 
22971       static double mp_tanh(_cimg_math_parser& mp) {
22972         return std::tanh(_mp_arg(2));
22973       }
22974 
22975       static double mp_trace(_cimg_math_parser& mp) {
22976         const double *ptrs = &_mp_arg(2) + 1;
22977         const unsigned int k = (unsigned int)mp.opcode[3];
22978         return CImg<doubleT>(ptrs,k,k,1,1,true).trace();
22979       }
22980 
22981       static double mp_transp(_cimg_math_parser& mp) {
22982         double *ptrd = &_mp_arg(1) + 1;
22983         const double *ptrs = &_mp_arg(2) + 1;
22984         const unsigned int
22985           k = (unsigned int)mp.opcode[3],
22986           l = (unsigned int)mp.opcode[4];
22987         CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptrs,k,l,1,1,true).get_transpose();
22988         return cimg::type<double>::nan();
22989       }
22990 
22991       static double mp_u(_cimg_math_parser& mp) {
22992         return cimg::rand(_mp_arg(2),_mp_arg(3));
22993       }
22994 
22995       static double mp_uppercase(_cimg_math_parser& mp) {
22996         return cimg::uppercase(_mp_arg(2));
22997       }
22998 
22999       static double mp_var(_cimg_math_parser& mp) {
23000         const unsigned int i_end = (unsigned int)mp.opcode[2];
23001         CImg<doubleT> vals(i_end - 3);
23002         double *p = vals.data();
23003         for (unsigned int i = 3; i<i_end; ++i) *(p++) = _mp_arg(i);
23004         return vals.variance();
23005       }
23006 
23007       static double mp_vector_copy(_cimg_math_parser& mp) {
23008         std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]);
23009         return cimg::type<double>::nan();
23010       }
23011 
23012       static double mp_vector_crop(_cimg_math_parser& mp) {
23013         double *const ptrd = &_mp_arg(1) + 1;
23014         const double *const ptrs = &_mp_arg(2) + 1;
23015         const longT
23016           length = (longT)mp.opcode[3],
23017           start = (longT)_mp_arg(4),
23018           sublength = (longT)mp.opcode[5];
23019         if (start<0 || start + sublength>length)
23020           throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': "
23021                                       "Out-of-bounds sub-vector request "
23022                                       "(length: %ld, start: %ld, sub-length: %ld).",
23023                                       mp.imgin.pixel_type(),length,start,sublength);
23024         std::memcpy(ptrd,ptrs + start,sublength*sizeof(double));
23025         return cimg::type<double>::nan();
23026       }
23027 
23028       static double mp_vector_init(_cimg_math_parser& mp) {
23029         unsigned int
23030           ptrs = 4U,
23031           ptrd = (unsigned int)mp.opcode[1] + 1,
23032           siz = (unsigned int)mp.opcode[3];
23033         switch (mp.opcode[2] - 4) {
23034         case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given
23035         case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break;
23036         default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; }
23037         }
23038         return cimg::type<double>::nan();
23039       }
23040 
23041       static double mp_vector_eq(_cimg_math_parser& mp) {
23042         const double
23043           *ptr1 = &_mp_arg(2) + 1,
23044           *ptr2 = &_mp_arg(4) + 1;
23045         unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n;
23046         const int N = (int)_mp_arg(6);
23047         const bool case_sensitive = (bool)_mp_arg(7);
23048         bool still_equal = true;
23049         double value;
23050         if (!N) return true;
23051 
23052         // Compare all values.
23053         if (N<0) {
23054           if (p1>0 && p2>0) { // Vector == vector
23055             if (p1!=p2) return false;
23056             if (case_sensitive)
23057               while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++);
23058             else
23059               while (still_equal && p1--)
23060                 still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
23061             return still_equal;
23062           } else if (p1>0 && !p2) { // Vector == scalar
23063             value = _mp_arg(4);
23064             if (!case_sensitive) value = cimg::lowercase(value);
23065             while (still_equal && p1--) still_equal = *(ptr1++)==value;
23066             return still_equal;
23067           } else if (!p1 && p2>0) { // Scalar == vector
23068             value = _mp_arg(2);
23069             if (!case_sensitive) value = cimg::lowercase(value);
23070             while (still_equal && p2--) still_equal = *(ptr2++)==value;
23071             return still_equal;
23072           } else { // Scalar == scalar
23073             if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
23074             else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
23075           }
23076         }
23077 
23078         // Compare only first N values.
23079         if (p1>0 && p2>0) { // Vector == vector
23080           n = cimg::min((unsigned int)N,p1,p2);
23081           if (case_sensitive)
23082             while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++);
23083           else
23084             while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
23085           return still_equal;
23086         } else if (p1>0 && !p2) { // Vector == scalar
23087           n = std::min((unsigned int)N,p1);
23088           value = _mp_arg(4);
23089           if (!case_sensitive) value = cimg::lowercase(value);
23090           while (still_equal && n--) still_equal = *(ptr1++)==value;
23091           return still_equal;
23092         } else if (!p1 && p2>0) { // Scalar == vector
23093           n = std::min((unsigned int)N,p2);
23094           value = _mp_arg(2);
23095           if (!case_sensitive) value = cimg::lowercase(value);
23096           while (still_equal && n--) still_equal = *(ptr2++)==value;
23097           return still_equal;
23098         }  // Scalar == scalar
23099         if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
23100         return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
23101       }
23102 
23103       static double mp_vector_off(_cimg_math_parser& mp) {
23104         const unsigned int
23105           ptr = (unsigned int)mp.opcode[2] + 1,
23106           siz = (unsigned int)mp.opcode[3];
23107         const int off = (int)_mp_arg(4);
23108         return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan();
23109       }
23110 
23111       static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector)
23112         unsigned int
23113           siz = (unsigned int)mp.opcode[2],
23114           ptrs = (unsigned int)mp.opcode[5] + 1;
23115         double *ptrd = &_mp_arg(1) + 1;
23116         mp_func op = (mp_func)mp.opcode[3];
23117         CImg<ulongT> l_opcode(4);
23118         l_opcode[2] = mp.opcode[4]; // Scalar argument1
23119         l_opcode.swap(mp.opcode);
23120         ulongT &argument2 = mp.opcode[3];
23121         while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); }
23122         l_opcode.swap(mp.opcode);
23123         return cimg::type<double>::nan();
23124       }
23125 
23126       static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector)
23127         unsigned int
23128           siz = (unsigned int)mp.opcode[2],
23129           ptrs = (unsigned int)mp.opcode[4] + 1;
23130         double *ptrd = &_mp_arg(1) + 1;
23131         mp_func op = (mp_func)mp.opcode[3];
23132         CImg<ulongT> l_opcode(1,3);
23133         l_opcode.swap(mp.opcode);
23134         ulongT &argument = mp.opcode[2];
23135         while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); }
23136         l_opcode.swap(mp.opcode);
23137         return cimg::type<double>::nan();
23138       }
23139 
23140       static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar)
23141         unsigned int
23142           siz = (unsigned int)mp.opcode[2],
23143           ptrs = (unsigned int)mp.opcode[4] + 1;
23144         double *ptrd = &_mp_arg(1) + 1;
23145         mp_func op = (mp_func)mp.opcode[3];
23146         CImg<ulongT> l_opcode(1,4);
23147         l_opcode[3] = mp.opcode[5]; // Scalar argument2
23148         l_opcode.swap(mp.opcode);
23149         ulongT &argument1 = mp.opcode[2];
23150         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
23151         l_opcode.swap(mp.opcode);
23152         return cimg::type<double>::nan();
23153       }
23154 
23155       static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar)
23156         unsigned int
23157           siz = (unsigned int)mp.opcode[2],
23158           ptrs = (unsigned int)mp.opcode[4] + 1;
23159         double *ptrd = &_mp_arg(1) + 1;
23160         mp_func op = (mp_func)mp.opcode[3];
23161         CImg<ulongT> l_opcode(1,5);
23162         l_opcode[3] = mp.opcode[5]; // Scalar argument2
23163         l_opcode[4] = mp.opcode[6]; // Scalar argument3
23164         l_opcode.swap(mp.opcode);
23165         ulongT &argument1 = mp.opcode[2];
23166         while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); }
23167         l_opcode.swap(mp.opcode);
23168         return cimg::type<double>::nan();
23169       }
23170 
23171       static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector)
23172         unsigned int
23173           siz = (unsigned int)mp.opcode[2],
23174           ptrs1 = (unsigned int)mp.opcode[4] + 1,
23175           ptrs2 = (unsigned int)mp.opcode[5] + 1;
23176         double *ptrd = &_mp_arg(1) + 1;
23177         mp_func op = (mp_func)mp.opcode[3];
23178         CImg<ulongT> l_opcode(1,4);
23179         l_opcode.swap(mp.opcode);
23180         ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3];
23181         while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); }
23182         l_opcode.swap(mp.opcode);
23183         return cimg::type<double>::nan();
23184       }
23185 
23186       static double mp_vector_neq(_cimg_math_parser& mp) {
23187         return !mp_vector_eq(mp);
23188       }
23189 
23190       static double mp_vector_print(_cimg_math_parser& mp) {
23191         const bool print_string = (bool)mp.opcode[4];
23192         cimg_pragma_openmp(critical(mp_vector_print))
23193         {
23194           CImg<charT> expr(mp.opcode[2] - 5);
23195           const ulongT *ptrs = mp.opcode._data + 5;
23196           cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
23197           cimg::strellipsize(expr);
23198           unsigned int
23199             ptr = (unsigned int)mp.opcode[1] + 1,
23200             siz0 = (unsigned int)mp.opcode[3],
23201             siz = siz0;
23202           cimg::mutex(6);
23203           std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",expr._data);
23204           unsigned int count = 0;
23205           while (siz-->0) {
23206             if (count>=64 && siz>=64) {
23207               std::fprintf(cimg::output(),"...,");
23208               ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64;
23209               siz = 64;
23210             } else std::fprintf(cimg::output(),"%g%s",mp.mem[ptr++],siz?",":"");
23211             ++count;
23212           }
23213           if (print_string) {
23214             CImg<charT> str(siz0 + 1);
23215             ptr = (unsigned int)mp.opcode[1] + 1;
23216             for (unsigned int k = 0; k<siz0; ++k) str[k] = (char)mp.mem[ptr++];
23217             str[siz0] = 0;
23218             cimg::strellipsize(str,1024,false);
23219             std::fprintf(cimg::output()," ] = '%s' (size: %u)",str._data,siz0);
23220           } else std::fprintf(cimg::output()," ] (size: %u)",siz0);
23221           std::fflush(cimg::output());
23222           cimg::mutex(6,0);
23223         }
23224         return cimg::type<double>::nan();
23225       }
23226 
23227       static double mp_vector_resize(_cimg_math_parser& mp) {
23228         double *const ptrd = &_mp_arg(1) + 1;
23229         const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4];
23230         const int
23231           interpolation = (int)_mp_arg(5),
23232           boundary_conditions = (int)_mp_arg(6);
23233         if (p2) { // Resize vector
23234           const double *const ptrs = &_mp_arg(3) + 1;
23235           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true).
23236             get_resize(p1,1,1,1,interpolation,boundary_conditions);
23237         } else { // Resize scalar
23238           const double value = _mp_arg(3);
23239           CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation,
23240                                                                                   boundary_conditions);
23241         }
23242         return cimg::type<double>::nan();
23243       }
23244 
23245       static double mp_vector_reverse(_cimg_math_parser& mp) {
23246         double *const ptrd = &_mp_arg(1) + 1;
23247         const double *const ptrs = &_mp_arg(2) + 1;
23248         const unsigned int p1 = (unsigned int)mp.opcode[3];
23249         CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x');
23250         return cimg::type<double>::nan();
23251       }
23252 
23253       static double mp_vector_set_off(_cimg_math_parser& mp) {
23254         const unsigned int
23255           ptr = (unsigned int)mp.opcode[2] + 1,
23256           siz = (unsigned int)mp.opcode[3];
23257         const int off = (int)_mp_arg(4);
23258         if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(5);
23259         return _mp_arg(5);
23260       }
23261 
23262       static double mp_vtos(_cimg_math_parser& mp) {
23263         double *ptrd = &_mp_arg(1) + 1;
23264         const unsigned int
23265           sizd = (unsigned int)mp.opcode[2],
23266           sizs = (unsigned int)mp.opcode[4];
23267         const int nb_digits = (int)_mp_arg(5);
23268         CImg<charT> format(8);
23269         switch (nb_digits) {
23270         case -1 : std::strcpy(format,"%g"); break;
23271         case 0 : std::strcpy(format,"%.17g"); break;
23272         default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
23273         }
23274         CImg<charT> str;
23275         if (sizs) { // Vector expression
23276           const double *ptrs = &_mp_arg(3) + 1;
23277           CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
23278         } else { // Scalar expression
23279           str.assign(sizd + 1);
23280           cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
23281         }
23282         const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1);
23283         CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
23284         return cimg::type<double>::nan();
23285       }
23286 
23287       static double mp_whiledo(_cimg_math_parser& mp) {
23288         const ulongT
23289           mem_body = mp.opcode[1],
23290           mem_cond = mp.opcode[2];
23291         const CImg<ulongT>
23292           *const p_cond = ++mp.p_code,
23293           *const p_body = p_cond + mp.opcode[3],
23294           *const p_end = p_body + mp.opcode[4];
23295         const unsigned int vsiz = (unsigned int)mp.opcode[5];
23296         bool is_cond = false;
23297         if (mp.opcode[6]) { // Set default value for result and condition if necessary
23298           if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
23299           else mp.mem[mem_body] = cimg::type<double>::nan();
23300         }
23301         if (mp.opcode[7]) mp.mem[mem_cond] = 0;
23302         const unsigned int _break_type = mp.break_type;
23303         mp.break_type = 0;
23304         do {
23305           for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
23306             mp.opcode._data = mp.p_code->_data;
23307             const ulongT target = mp.opcode[1];
23308             mp.mem[target] = _cimg_mp_defunc(mp);
23309           }
23310           if (mp.break_type==1) break;
23311           is_cond = (bool)mp.mem[mem_cond];
23312           if (is_cond && !mp.break_type) // Evaluate body
23313             for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
23314               mp.opcode._data = mp.p_code->_data;
23315               const ulongT target = mp.opcode[1];
23316               mp.mem[target] = _cimg_mp_defunc(mp);
23317             }
23318           if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
23319         } while (is_cond);
23320 
23321         mp.break_type = _break_type;
23322         mp.p_code = p_end - 1;
23323         return mp.mem[mem_body];
23324       }
23325 
23326       static double mp_Ioff(_cimg_math_parser& mp) {
23327         double *ptrd = &_mp_arg(1) + 1;
23328         const unsigned int
23329           boundary_conditions = (unsigned int)_mp_arg(3),
23330           vsiz = (unsigned int)mp.opcode[4];
23331         const CImg<T> &img = mp.imgin;
23332         const longT
23333           off = (longT)_mp_arg(2),
23334           whd = (longT)img.width()*img.height()*img.depth();
23335         const T *ptrs;
23336         if (off>=0 && off<whd) {
23337           ptrs = &img[off];
23338           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23339           return cimg::type<double>::nan();
23340         }
23341         if (img._data) switch (boundary_conditions) {
23342           case 3 : { // Mirror
23343             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
23344             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
23345             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23346             return cimg::type<double>::nan();
23347           }
23348           case 2 : // Periodic
23349             ptrs = &img[cimg::mod(off,whd)];
23350             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23351             return cimg::type<double>::nan();
23352           case 1 : // Neumann
23353             ptrs = off<0?&img[0]:&img[whd - 1];
23354             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23355             return cimg::type<double>::nan();
23356           default : // Dirichlet
23357             std::memset(ptrd,0,vsiz*sizeof(double));
23358             return cimg::type<double>::nan();
23359           }
23360         std::memset(ptrd,0,vsiz*sizeof(double));
23361         return cimg::type<double>::nan();
23362       }
23363 
23364       static double mp_Ixyz(_cimg_math_parser& mp) {
23365         double *ptrd = &_mp_arg(1) + 1;
23366         const unsigned int
23367           interpolation = (unsigned int)_mp_arg(5),
23368           boundary_conditions = (unsigned int)_mp_arg(6),
23369           vsiz = (unsigned int)mp.opcode[7];
23370         const CImg<T> &img = mp.imgin;
23371         const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4);
23372         const ulongT whd = (ulongT)img._width*img._height*img._depth;
23373         const T *ptrs;
23374         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
23375           case 3 : { // Mirror
23376             const int
23377               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
23378               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
23379               cx = mx<img.width()?mx:w2 - mx - 1,
23380               cy = my<img.height()?my:h2 - my - 1,
23381               cz = mz<img.depth()?mz:d2 - mz - 1;
23382             ptrs = &img(cx,cy,cz);
23383             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23384           } break;
23385           case 2 : { // Periodic
23386             const int
23387               cx = cimg::mod((int)x,img.width()),
23388               cy = cimg::mod((int)y,img.height()),
23389               cz = cimg::mod((int)z,img.depth());
23390             ptrs = &img(cx,cy,cz);
23391             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23392           } break;
23393           case 1 : { // Neumann
23394             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
23395             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23396           } break;
23397           default : // Dirichlet
23398             if (img.containsXYZC((int)x,(int)y,(int)z)) {
23399               ptrs = &img((int)x,(int)y,(int)z);
23400               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23401             } else std::memset(ptrd,0,vsiz*sizeof(double));
23402           } else switch (boundary_conditions) { // Linear interpolation
23403           case 3 : { // Mirror
23404             const float
23405               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
23406               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
23407               cx = mx<img.width()?mx:w2 - mx - 1,
23408               cy = my<img.height()?my:h2 - my - 1,
23409               cz = mz<img.depth()?mz:d2 - mz - 1;
23410             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
23411           } break;
23412           case 2 : { // Periodic
23413             const float
23414               cx = cimg::mod((float)x,(float)img.width()),
23415               cy = cimg::mod((float)y,(float)img.height()),
23416               cz = cimg::mod((float)z,(float)img.depth());
23417             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
23418           } break;
23419           case 1 : // Neumann
23420             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
23421             break;
23422           default : // Dirichlet
23423             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
23424           }
23425         return cimg::type<double>::nan();
23426       }
23427 
23428       static double mp_Joff(_cimg_math_parser& mp) {
23429         double *ptrd = &_mp_arg(1) + 1;
23430         const unsigned int
23431           boundary_conditions = (unsigned int)_mp_arg(3),
23432           vsiz = (unsigned int)mp.opcode[4];
23433         const CImg<T> &img = mp.imgin;
23434         const int
23435           ox = (int)mp.mem[_cimg_mp_slot_x],
23436           oy = (int)mp.mem[_cimg_mp_slot_y],
23437           oz = (int)mp.mem[_cimg_mp_slot_z];
23438         const longT
23439           off = img.offset(ox,oy,oz) + (longT)_mp_arg(2),
23440           whd = (longT)img.width()*img.height()*img.depth();
23441         const T *ptrs;
23442         if (off>=0 && off<whd) {
23443           ptrs = &img[off];
23444           cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23445           return cimg::type<double>::nan();
23446         }
23447         if (img._data) switch (boundary_conditions) {
23448           case 3 : { // Mirror
23449             const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
23450             ptrs = &img[moff<whd?moff:whd2 - moff - 1];
23451             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23452             return cimg::type<double>::nan();
23453           }
23454           case 2 : // Periodic
23455             ptrs = &img[cimg::mod(off,whd)];
23456             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23457             return cimg::type<double>::nan();
23458           case 1 : // Neumann
23459             ptrs = off<0?&img[0]:&img[whd - 1];
23460             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
23461             return cimg::type<double>::nan();
23462           default : // Dirichlet
23463             std::memset(ptrd,0,vsiz*sizeof(double));
23464             return cimg::type<double>::nan();
23465           }
23466         std::memset(ptrd,0,vsiz*sizeof(double));
23467         return cimg::type<double>::nan();
23468       }
23469 
23470       static double mp_Jxyz(_cimg_math_parser& mp) {
23471         double *ptrd = &_mp_arg(1) + 1;
23472         const unsigned int
23473           interpolation = (unsigned int)_mp_arg(5),
23474           boundary_conditions = (unsigned int)_mp_arg(6),
23475           vsiz = (unsigned int)mp.opcode[7];
23476         const CImg<T> &img = mp.imgin;
23477         const double
23478           ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
23479           x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4);
23480         const ulongT whd = (ulongT)img._width*img._height*img._depth;
23481         const T *ptrs;
23482         if (interpolation==0) switch (boundary_conditions) { // Nearest neighbor interpolation
23483           case 3 : { // Mirror
23484             const int
23485               w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
23486               mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
23487               cx = mx<img.width()?mx:w2 - mx - 1,
23488               cy = my<img.height()?my:h2 - my - 1,
23489               cz = mz<img.depth()?mz:d2 - mz - 1;
23490             ptrs = &img(cx,cy,cz);
23491             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23492           } break;
23493           case 2 : { // Periodic
23494             const int
23495               cx = cimg::mod((int)x,img.width()),
23496               cy = cimg::mod((int)y,img.height()),
23497               cz = cimg::mod((int)z,img.depth());
23498             ptrs = &img(cx,cy,cz);
23499             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23500           } break;
23501           case 1 : { // Neumann
23502             ptrs = &img._atXYZ((int)x,(int)y,(int)z);
23503             cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23504           } break;
23505           default : // Dirichlet
23506             if (img.containsXYZC((int)x,(int)y,(int)z)) {
23507               ptrs = &img((int)x,(int)y,(int)z);
23508               cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
23509             } else std::memset(ptrd,0,vsiz*sizeof(double));
23510           } else switch (boundary_conditions) { // Linear interpolation
23511           case 3 : { // Mirror
23512             const float
23513               w2 = 2.0f*img.width(), h2 = 2.0f*img.height(), d2 = 2.0f*img.depth(),
23514               mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
23515               cx = mx<img.width()?mx:w2 - mx - 1,
23516               cy = my<img.height()?my:h2 - my - 1,
23517               cz = mz<img.depth()?mz:d2 - mz - 1;
23518             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
23519           } break;
23520           case 2 : { // Periodic
23521             const float
23522               cx = cimg::mod((float)x,(float)img.width()),
23523               cy = cimg::mod((float)y,(float)img.height()),
23524               cz = cimg::mod((float)z,(float)img.depth());
23525             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
23526           } break;
23527           case 1 : // Neumann
23528             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
23529             break;
23530           default : // Dirichlet
23531             cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
23532           }
23533         return cimg::type<double>::nan();
23534       }
23535 
23536 #undef _mp_arg
23537 
23538     }; // struct _cimg_math_parser {}
23539 
23540 #define _cimg_create_pointwise_functions(name,func,openmp_size) \
23541     CImg<T>& name() { \
23542       if (is_empty()) return *this; \
23543       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=openmp_size)) \
23544       cimg_rof(*this,ptrd,T) *ptrd = (T)func((double)*ptrd); \
23545       return *this; \
23546     } \
23547     CImg<Tfloat> get_##name() const { \
23548       return CImg<Tfloat>(*this,false).name(); \
23549     }
23550 
23551     //! Compute the square value of each pixel value.
23552     /**
23553        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square value \f$I_{(x,y,z,c)}^2\f$.
23554        \note
23555        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23556        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23557        \par Example
23558        \code
23559        const CImg<float> img("reference.jpg");
23560        (img,img.get_sqr().normalize(0,255)).display();
23561        \endcode
23562        \image html ref_sqr.jpg
23563     **/
23564     _cimg_create_pointwise_functions(sqr,cimg::sqr,524288)
23565 
23566     //! Compute the square root of each pixel value.
23567     /**
23568        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square root \f$\sqrt{I_{(x,y,z,c)}}\f$.
23569        \note
23570        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23571        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23572        \par Example
23573        \code
23574        const CImg<float> img("reference.jpg");
23575        (img,img.get_sqrt().normalize(0,255)).display();
23576        \endcode
23577        \image html ref_sqrt.jpg
23578     **/
23579     _cimg_create_pointwise_functions(sqrt,std::sqrt,8192)
23580 
23581     //! Compute the exponential of each pixel value.
23582     /**
23583        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its exponential \f$e^{I_{(x,y,z,c)}}\f$.
23584        \note
23585        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23586        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23587     **/
23588     _cimg_create_pointwise_functions(exp,std::exp,4096)
23589 
23590     //! Compute the logarithm of each pixel value.
23591     /**
23592        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm
23593        \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$.
23594        \note
23595        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23596        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23597     **/
23598     _cimg_create_pointwise_functions(log,std::log,262144)
23599 
23600     //! Compute the base-2 logarithm of each pixel value.
23601     /**
23602        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm
23603        \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$.
23604        \note
23605        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23606        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23607     **/
23608     _cimg_create_pointwise_functions(log2,cimg::log2,4096)
23609 
23610     //! Compute the base-10 logarithm of each pixel value.
23611     /**
23612        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm
23613        \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$.
23614        \note
23615        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23616        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23617     **/
23618     _cimg_create_pointwise_functions(log10,std::log10,4096)
23619 
23620     //! Compute the absolute value of each pixel value.
23621     /**
23622        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its absolute value \f$|I_{(x,y,z,c)}|\f$.
23623        \note
23624        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23625        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23626     **/
23627     _cimg_create_pointwise_functions(abs,cimg::abs,524288)
23628 
23629     //! Compute the sign of each pixel value.
23630     /**
23631        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign
23632        \f$\mathrm{sign}(I_{(x,y,z,c)})\f$.
23633        \note
23634        - The sign is set to:
23635          - \c 1 if pixel value is strictly positive.
23636          - \c -1 if pixel value is strictly negative.
23637          - \c 0 if pixel value is equal to \c 0.
23638        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23639        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23640     **/
23641     _cimg_create_pointwise_functions(sign,cimg::sign,32768)
23642 
23643     //! Compute the cosine of each pixel value.
23644     /**
23645        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its cosine \f$\cos(I_{(x,y,z,c)})\f$.
23646        \note
23647        - Pixel values are regarded as being in \e radian.
23648        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23649        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23650     **/
23651     _cimg_create_pointwise_functions(cos,std::cos,8192)
23652 
23653     //! Compute the sine of each pixel value.
23654     /**
23655        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sine \f$\sin(I_{(x,y,z,c)})\f$.
23656        \note
23657        - Pixel values are regarded as being in \e radian.
23658        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23659        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23660     **/
23661     _cimg_create_pointwise_functions(sin,std::sin,8192)
23662 
23663     //! Compute the sinc of each pixel value.
23664     /**
23665        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc
23666        \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$.
23667        \note
23668        - Pixel values are regarded as being exin \e radian.
23669        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23670        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23671     **/
23672     _cimg_create_pointwise_functions(sinc,cimg::sinc,2048)
23673 
23674     //! Compute the tangent of each pixel value.
23675     /**
23676        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its tangent \f$\tan(I_{(x,y,z,c)})\f$.
23677        \note
23678        - Pixel values are regarded as being exin \e radian.
23679        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23680        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23681     **/
23682     _cimg_create_pointwise_functions(tan,std::tan,2048)
23683 
23684     //! Compute the hyperbolic cosine of each pixel value.
23685     /**
23686        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine
23687        \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$.
23688        \note
23689        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23690        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23691     **/
23692     _cimg_create_pointwise_functions(cosh,std::cosh,2048)
23693 
23694     //! Compute the hyperbolic sine of each pixel value.
23695     /**
23696        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine
23697        \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$.
23698        \note
23699        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23700        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23701     **/
23702     _cimg_create_pointwise_functions(sinh,std::sinh,2048)
23703 
23704     //! Compute the hyperbolic tangent of each pixel value.
23705     /**
23706        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent
23707        \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$.
23708        \note
23709        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23710        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23711     **/
23712     _cimg_create_pointwise_functions(tanh,std::tanh,2048)
23713 
23714     //! Compute the arccosine of each pixel value.
23715     /**
23716        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine
23717        \f$\mathrm{acos}(I_{(x,y,z,c)})\f$.
23718        \note
23719        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23720        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23721     **/
23722     _cimg_create_pointwise_functions(acos,std::acos,8192)
23723 
23724     //! Compute the arcsine of each pixel value.
23725     /**
23726        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine
23727        \f$\mathrm{asin}(I_{(x,y,z,c)})\f$.
23728        \note
23729        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23730        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23731     **/
23732     _cimg_create_pointwise_functions(asin,std::asin,8192)
23733 
23734     //! Compute the arctangent of each pixel value.
23735     /**
23736        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent
23737        \f$\mathrm{atan}(I_{(x,y,z,c)})\f$.
23738        \note
23739        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23740        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23741     **/
23742     _cimg_create_pointwise_functions(atan,std::atan,8192)
23743 
23744     //! Compute the arctangent2 of each pixel value.
23745     /**
23746        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2
23747        \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$.
23748        \param img Image whose pixel values specify the second argument of the \c atan2() function.
23749        \note
23750        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23751        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23752        \par Example
23753        \code
23754        const CImg<float>
23755           img_x(100,100,1,1,"x-w/2",false),   // Define an horizontal centered gradient, from '-width/2' to 'width/2'.
23756           img_y(100,100,1,1,"y-h/2",false),   // Define a vertical centered gradient, from '-height/2' to 'height/2'.
23757           img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value.
23758        (img_x,img_y,img_atan2).display();
23759        \endcode
23760     **/
23761     template<typename t>
23762     CImg<T>& atan2(const CImg<t>& img) {
23763       const ulongT siz = size(), isiz = img.size();
23764       if (siz && isiz) {
23765         if (is_overlapped(img)) return atan2(+img);
23766         T *ptrd = _data, *const ptre = _data + siz;
23767         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
23768           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
23769             *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
23770         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
23771       }
23772       return *this;
23773     }
23774 
23775     //! Compute the arctangent2 of each pixel value \newinstance.
23776     template<typename t>
23777     CImg<Tfloat> get_atan2(const CImg<t>& img) const {
23778       return CImg<Tfloat>(*this,false).atan2(img);
23779     }
23780 
23781     //! Compute the hyperbolic arccosine of each pixel value.
23782     /**
23783        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh
23784        \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$.
23785        \note
23786        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23787        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23788     **/
23789     _cimg_create_pointwise_functions(acosh,cimg::acosh,8192)
23790 
23791     //! Compute the hyperbolic arcsine of each pixel value.
23792     /**
23793        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine
23794        \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$.
23795        \note
23796        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23797        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23798     **/
23799     _cimg_create_pointwise_functions(asinh,cimg::asinh,8192)
23800 
23801     //! Compute the hyperbolic arctangent of each pixel value.
23802     /**
23803        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent
23804        \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$.
23805        \note
23806        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23807        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23808     **/
23809     _cimg_create_pointwise_functions(atanh,cimg::atanh,8192)
23810 
23811     //! In-place pointwise multiplication.
23812     /**
23813        Compute the pointwise multiplication between the image instance and the specified input image \c img.
23814        \param img Input image, as the second operand of the multiplication.
23815        \note
23816        - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication
23817          instead of an addition.
23818        - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
23819        \par Example
23820        \code
23821        CImg<float>
23822          img("reference.jpg"),
23823          shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
23824        shade.normalize(0,1);
23825        (img,shade,img.get_mul(shade)).display();
23826        \endcode
23827     **/
23828     template<typename t>
23829     CImg<T>& mul(const CImg<t>& img) {
23830       const ulongT siz = size(), isiz = img.size();
23831       if (siz && isiz) {
23832         if (is_overlapped(img)) return mul(+img);
23833         T *ptrd = _data, *const ptre = _data + siz;
23834         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
23835           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
23836             *ptrd = (T)(*ptrd * *(ptrs++));
23837         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
23838       }
23839       return *this;
23840     }
23841 
23842     //! In-place pointwise multiplication \newinstance.
23843     template<typename t>
23844     CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
23845       return CImg<_cimg_Tt>(*this,false).mul(img);
23846     }
23847 
23848     //! In-place pointwise division.
23849     /**
23850        Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
23851     **/
23852     template<typename t>
23853     CImg<T>& div(const CImg<t>& img) {
23854       const ulongT siz = size(), isiz = img.size();
23855       if (siz && isiz) {
23856         if (is_overlapped(img)) return div(+img);
23857         T *ptrd = _data, *const ptre = _data + siz;
23858         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
23859           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
23860             *ptrd = (T)(*ptrd / *(ptrs++));
23861         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
23862       }
23863       return *this;
23864     }
23865 
23866     //! In-place pointwise division \newinstance.
23867     template<typename t>
23868     CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
23869       return CImg<_cimg_Tt>(*this,false).div(img);
23870     }
23871 
23872     //! Raise each pixel value to a specified power.
23873     /**
23874        Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its power \f$I_{(x,y,z,c)}^p\f$.
23875        \param p Exponent value.
23876        \note
23877        - The \inplace of this method statically casts the computed values to the pixel type \c T.
23878        - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
23879        \par Example
23880        \code
23881        const CImg<float>
23882          img0("reference.jpg"),           // Load reference color image.
23883          img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8.
23884          img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5.
23885        (img0,img1,img2).display();
23886        \endcode
23887     **/
23888     CImg<T>& pow(const double p) {
23889       if (is_empty()) return *this;
23890       if (p==-4) {
23891         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23892         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val*val)); }
23893         return *this;
23894       }
23895       if (p==-3) {
23896         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23897         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val*val)); }
23898         return *this;
23899       }
23900       if (p==-2) {
23901         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23902         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/(val*val)); }
23903         return *this;
23904       }
23905       if (p==-1) {
23906         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23907         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1.0/val); }
23908         return *this;
23909       }
23910       if (p==-0.5) {
23911         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
23912         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(1/std::sqrt((double)val)); }
23913         return *this;
23914       }
23915       if (p==0) return fill((T)1);
23916       if (p==0.25) return sqrt().sqrt();
23917       if (p==0.5) return sqrt();
23918       if (p==1) return *this;
23919       if (p==2) return sqr();
23920       if (p==3) {
23921         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=262144))
23922         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; }
23923         return *this;
23924       }
23925       if (p==4) {
23926         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=131072))
23927         cimg_rof(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; }
23928         return *this;
23929       }
23930       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=1024))
23931       cimg_rof(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p);
23932       return *this;
23933     }
23934 
23935     //! Raise each pixel value to a specified power \newinstance.
23936     CImg<Tfloat> get_pow(const double p) const {
23937       return CImg<Tfloat>(*this,false).pow(p);
23938     }
23939 
23940     //! Raise each pixel value to a power, specified from an expression.
23941     /**
23942        Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
23943     **/
23944     CImg<T>& pow(const char *const expression) {
23945       return pow((+*this)._fill(expression,true,true,0,0,"pow",this));
23946     }
23947 
23948     //! Raise each pixel value to a power, specified from an expression \newinstance.
23949     CImg<Tfloat> get_pow(const char *const expression) const {
23950       return CImg<Tfloat>(*this,false).pow(expression);
23951     }
23952 
23953     //! Raise each pixel value to a power, pointwisely specified from another image.
23954     /**
23955        Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
23956     **/
23957     template<typename t>
23958     CImg<T>& pow(const CImg<t>& img) {
23959       const ulongT siz = size(), isiz = img.size();
23960       if (siz && isiz) {
23961         if (is_overlapped(img)) return pow(+img);
23962         T *ptrd = _data, *const ptre = _data + siz;
23963         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
23964           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
23965             *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
23966         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
23967       }
23968       return *this;
23969     }
23970 
23971     //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
23972     template<typename t>
23973     CImg<Tfloat> get_pow(const CImg<t>& img) const {
23974       return CImg<Tfloat>(*this,false).pow(img);
23975     }
23976 
23977     //! Compute the bitwise left rotation of each pixel value.
23978     /**
23979        Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
23980     **/
23981     CImg<T>& rol(const unsigned int n=1) {
23982       if (is_empty()) return *this;
23983       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
23984       cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n);
23985       return *this;
23986     }
23987 
23988     //! Compute the bitwise left rotation of each pixel value \newinstance.
23989     CImg<T> get_rol(const unsigned int n=1) const {
23990       return (+*this).rol(n);
23991     }
23992 
23993     //! Compute the bitwise left rotation of each pixel value.
23994     /**
23995        Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
23996     **/
23997     CImg<T>& rol(const char *const expression) {
23998       return rol((+*this)._fill(expression,true,true,0,0,"rol",this));
23999     }
24000 
24001     //! Compute the bitwise left rotation of each pixel value \newinstance.
24002     CImg<T> get_rol(const char *const expression) const {
24003       return (+*this).rol(expression);
24004     }
24005 
24006     //! Compute the bitwise left rotation of each pixel value.
24007     /**
24008        Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
24009     **/
24010     template<typename t>
24011     CImg<T>& rol(const CImg<t>& img) {
24012       const ulongT siz = size(), isiz = img.size();
24013       if (siz && isiz) {
24014         if (is_overlapped(img)) return rol(+img);
24015         T *ptrd = _data, *const ptre = _data + siz;
24016         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
24017           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
24018             *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
24019         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
24020       }
24021       return *this;
24022     }
24023 
24024     //! Compute the bitwise left rotation of each pixel value \newinstance.
24025     template<typename t>
24026     CImg<T> get_rol(const CImg<t>& img) const {
24027       return (+*this).rol(img);
24028     }
24029 
24030     //! Compute the bitwise right rotation of each pixel value.
24031     /**
24032        Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
24033     **/
24034     CImg<T>& ror(const unsigned int n=1) {
24035       if (is_empty()) return *this;
24036       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
24037       cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n);
24038       return *this;
24039     }
24040 
24041     //! Compute the bitwise right rotation of each pixel value \newinstance.
24042     CImg<T> get_ror(const unsigned int n=1) const {
24043       return (+*this).ror(n);
24044     }
24045 
24046     //! Compute the bitwise right rotation of each pixel value.
24047     /**
24048        Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
24049     **/
24050     CImg<T>& ror(const char *const expression) {
24051       return ror((+*this)._fill(expression,true,true,0,0,"ror",this));
24052     }
24053 
24054     //! Compute the bitwise right rotation of each pixel value \newinstance.
24055     CImg<T> get_ror(const char *const expression) const {
24056       return (+*this).ror(expression);
24057     }
24058 
24059     //! Compute the bitwise right rotation of each pixel value.
24060     /**
24061        Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
24062     **/
24063     template<typename t>
24064     CImg<T>& ror(const CImg<t>& img) {
24065       const ulongT siz = size(), isiz = img.size();
24066       if (siz && isiz) {
24067         if (is_overlapped(img)) return ror(+img);
24068         T *ptrd = _data, *const ptre = _data + siz;
24069         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
24070           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
24071             *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
24072         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
24073       }
24074       return *this;
24075     }
24076 
24077     //! Compute the bitwise right rotation of each pixel value \newinstance.
24078     template<typename t>
24079     CImg<T> get_ror(const CImg<t>& img) const {
24080       return (+*this).ror(img);
24081     }
24082 
24083     //! Pointwise min operator between instance image and a value.
24084     /**
24085        \param val Value used as the reference argument of the min operator.
24086        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24087        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$.
24088      **/
24089     CImg<T>& min(const T& val) {
24090       if (is_empty()) return *this;
24091       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
24092       cimg_rof(*this,ptrd,T) *ptrd = std::min(*ptrd,val);
24093       return *this;
24094     }
24095 
24096     //! Pointwise min operator between instance image and a value \newinstance.
24097     CImg<T> get_min(const T& val) const {
24098       return (+*this).min(val);
24099     }
24100 
24101     //! Pointwise min operator between two images.
24102     /**
24103        \param img Image used as the reference argument of the min operator.
24104        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24105        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
24106      **/
24107     template<typename t>
24108     CImg<T>& min(const CImg<t>& img) {
24109       const ulongT siz = size(), isiz = img.size();
24110       if (siz && isiz) {
24111         if (is_overlapped(img)) return min(+img);
24112         T *ptrd = _data, *const ptre = _data + siz;
24113         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
24114           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
24115             *ptrd = std::min((T)*(ptrs++),*ptrd);
24116         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::min((T)*(ptrs++),*ptrd);
24117       }
24118       return *this;
24119     }
24120 
24121     //! Pointwise min operator between two images \newinstance.
24122     template<typename t>
24123     CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
24124       return CImg<_cimg_Tt>(*this,false).min(img);
24125     }
24126 
24127     //! Pointwise min operator between an image and an expression.
24128     /**
24129        \param expression Math formula as a C-string.
24130        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24131        \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
24132     **/
24133     CImg<T>& min(const char *const expression) {
24134       return min((+*this)._fill(expression,true,true,0,0,"min",this));
24135     }
24136 
24137     //! Pointwise min operator between an image and an expression \newinstance.
24138     CImg<Tfloat> get_min(const char *const expression) const {
24139       return CImg<Tfloat>(*this,false).min(expression);
24140     }
24141 
24142     //! Pointwise max operator between instance image and a value.
24143     /**
24144        \param val Value used as the reference argument of the max operator.
24145        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24146        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$.
24147      **/
24148     CImg<T>& max(const T& val) {
24149       if (is_empty()) return *this;
24150       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
24151       cimg_rof(*this,ptrd,T) *ptrd = std::max(*ptrd,val);
24152       return *this;
24153     }
24154 
24155     //! Pointwise max operator between instance image and a value \newinstance.
24156     CImg<T> get_max(const T& val) const {
24157       return (+*this).max(val);
24158     }
24159 
24160     //! Pointwise max operator between two images.
24161     /**
24162        \param img Image used as the reference argument of the max operator.
24163        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24164        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
24165      **/
24166     template<typename t>
24167     CImg<T>& max(const CImg<t>& img) {
24168       const ulongT siz = size(), isiz = img.size();
24169       if (siz && isiz) {
24170         if (is_overlapped(img)) return max(+img);
24171         T *ptrd = _data, *const ptre = _data + siz;
24172         if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
24173           for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
24174             *ptrd = std::max((T)*(ptrs++),*ptrd);
24175         for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::max((T)*(ptrs++),*ptrd);
24176       }
24177       return *this;
24178     }
24179 
24180     //! Pointwise max operator between two images \newinstance.
24181     template<typename t>
24182     CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
24183       return CImg<_cimg_Tt>(*this,false).max(img);
24184     }
24185 
24186     //! Pointwise max operator between an image and an expression.
24187     /**
24188        \param expression Math formula as a C-string.
24189        \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
24190        \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
24191     **/
24192     CImg<T>& max(const char *const expression) {
24193       return max((+*this)._fill(expression,true,true,0,0,"max",this));
24194     }
24195 
24196     //! Pointwise max operator between an image and an expression \newinstance.
24197     CImg<Tfloat> get_max(const char *const expression) const {
24198       return CImg<Tfloat>(*this,false).max(expression);
24199     }
24200 
24201     //! Return a reference to the minimum pixel value.
24202     /**
24203      **/
24204     T& min() {
24205       if (is_empty())
24206         throw CImgInstanceException(_cimg_instance
24207                                     "min(): Empty instance.",
24208                                     cimg_instance);
24209       T *ptr_min = _data;
24210       T min_value = *ptr_min;
24211       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
24212       return *ptr_min;
24213     }
24214 
24215     //! Return a reference to the minimum pixel value \const.
24216     const T& min() const {
24217       if (is_empty())
24218         throw CImgInstanceException(_cimg_instance
24219                                     "min(): Empty instance.",
24220                                     cimg_instance);
24221       const T *ptr_min = _data;
24222       T min_value = *ptr_min;
24223       cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
24224       return *ptr_min;
24225     }
24226 
24227     //! Return a reference to the maximum pixel value.
24228     /**
24229      **/
24230     T& max() {
24231       if (is_empty())
24232         throw CImgInstanceException(_cimg_instance
24233                                     "max(): Empty instance.",
24234                                     cimg_instance);
24235       T *ptr_max = _data;
24236       T max_value = *ptr_max;
24237       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
24238       return *ptr_max;
24239     }
24240 
24241     //! Return a reference to the maximum pixel value \const.
24242     const T& max() const {
24243       if (is_empty())
24244         throw CImgInstanceException(_cimg_instance
24245                                     "max(): Empty instance.",
24246                                     cimg_instance);
24247       const T *ptr_max = _data;
24248       T max_value = *ptr_max;
24249       cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
24250       return *ptr_max;
24251     }
24252 
24253     //! Return a reference to the minimum pixel value as well as the maximum pixel value.
24254     /**
24255        \param[out] max_val Maximum pixel value.
24256     **/
24257     template<typename t>
24258     T& min_max(t& max_val) {
24259       if (is_empty())
24260         throw CImgInstanceException(_cimg_instance
24261                                     "min_max(): Empty instance.",
24262                                     cimg_instance);
24263       T *ptr_min = _data;
24264       T min_value = *ptr_min, max_value = min_value;
24265       cimg_for(*this,ptrs,T) {
24266         const T val = *ptrs;
24267         if (val<min_value) { min_value = val; ptr_min = ptrs; }
24268         if (val>max_value) max_value = val;
24269       }
24270       max_val = (t)max_value;
24271       return *ptr_min;
24272     }
24273 
24274     //! Return a reference to the minimum pixel value as well as the maximum pixel value \const.
24275     template<typename t>
24276     const T& min_max(t& max_val) const {
24277       if (is_empty())
24278         throw CImgInstanceException(_cimg_instance
24279                                     "min_max(): Empty instance.",
24280                                     cimg_instance);
24281       const T *ptr_min = _data;
24282       T min_value = *ptr_min, max_value = min_value;
24283       cimg_for(*this,ptrs,T) {
24284         const T val = *ptrs;
24285         if (val<min_value) { min_value = val; ptr_min = ptrs; }
24286         if (val>max_value) max_value = val;
24287       }
24288       max_val = (t)max_value;
24289       return *ptr_min;
24290     }
24291 
24292     //! Return a reference to the maximum pixel value as well as the minimum pixel value.
24293     /**
24294        \param[out] min_val Minimum pixel value.
24295     **/
24296     template<typename t>
24297     T& max_min(t& min_val) {
24298       if (is_empty())
24299         throw CImgInstanceException(_cimg_instance
24300                                     "max_min(): Empty instance.",
24301                                     cimg_instance);
24302       T *ptr_max = _data;
24303       T max_value = *ptr_max, min_value = max_value;
24304       cimg_for(*this,ptrs,T) {
24305         const T val = *ptrs;
24306         if (val>max_value) { max_value = val; ptr_max = ptrs; }
24307         if (val<min_value) min_value = val;
24308       }
24309       min_val = (t)min_value;
24310       return *ptr_max;
24311     }
24312 
24313     //! Return a reference to the maximum pixel value as well as the minimum pixel value \const.
24314     template<typename t>
24315     const T& max_min(t& min_val) const {
24316       if (is_empty())
24317         throw CImgInstanceException(_cimg_instance
24318                                     "max_min(): Empty instance.",
24319                                     cimg_instance);
24320       const T *ptr_max = _data;
24321       T max_value = *ptr_max, min_value = max_value;
24322       cimg_for(*this,ptrs,T) {
24323         const T val = *ptrs;
24324         if (val>max_value) { max_value = val; ptr_max = ptrs; }
24325         if (val<min_value) min_value = val;
24326       }
24327       min_val = (t)min_value;
24328       return *ptr_max;
24329     }
24330 
24331     //! Return the kth smallest pixel value.
24332     /**
24333        \param k Rank of the search smallest element.
24334     **/
24335     T kth_smallest(const ulongT k) const {
24336       if (is_empty())
24337         throw CImgInstanceException(_cimg_instance
24338                                     "kth_smallest(): Empty instance.",
24339                                     cimg_instance);
24340       CImg<T> arr(*this,false);
24341       ulongT l = 0, ir = size() - 1;
24342       for ( ; ; ) {
24343         if (ir<=l + 1) {
24344           if (ir==l + 1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
24345           return arr[k];
24346         } else {
24347           const ulongT mid = (l + ir)>>1;
24348           cimg::swap(arr[mid],arr[l + 1]);
24349           if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
24350           if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]);
24351           if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]);
24352           ulongT i = l + 1, j = ir;
24353           const T pivot = arr[l + 1];
24354           for ( ; ; ) {
24355             do ++i; while (arr[i]<pivot);
24356             do --j; while (arr[j]>pivot);
24357             if (j<i) break;
24358             cimg::swap(arr[i],arr[j]);
24359           }
24360           arr[l + 1] = arr[j];
24361           arr[j] = pivot;
24362           if (j>=k) ir = j - 1;
24363           if (j<=k) l = i;
24364         }
24365       }
24366     }
24367 
24368     //! Return the median pixel value.
24369     /**
24370      **/
24371     T median() const {
24372       if (is_empty())
24373         throw CImgInstanceException(_cimg_instance
24374                                     "median(): Empty instance.",
24375                                     cimg_instance);
24376       const ulongT s = size();
24377       switch (s) {
24378       case 1 : return _data[0];
24379       case 2 : return cimg::median(_data[0],_data[1]);
24380       case 3 : return cimg::median(_data[0],_data[1],_data[2]);
24381       case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]);
24382       case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]);
24383       case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]);
24384       case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8],
24385                                     _data[9],_data[10],_data[11],_data[12]);
24386       }
24387       const T res = kth_smallest(s>>1);
24388       return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2);
24389     }
24390 
24391     //! Return the product of all the pixel values.
24392     /**
24393      **/
24394     double product() const {
24395       if (is_empty()) return 0;
24396       double res = 1;
24397       cimg_for(*this,ptrs,T) res*=(double)*ptrs;
24398       return res;
24399     }
24400 
24401     //! Return the sum of all the pixel values.
24402     /**
24403      **/
24404     double sum() const {
24405       double res = 0;
24406       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
24407       return res;
24408     }
24409 
24410     //! Return the average pixel value.
24411     /**
24412      **/
24413     double mean() const {
24414       double res = 0;
24415       cimg_for(*this,ptrs,T) res+=(double)*ptrs;
24416       return res/size();
24417     }
24418 
24419     //! Return the variance of the pixel values.
24420     /**
24421        \param variance_method Method used to estimate the variance. Can be:
24422        - \c 0: Second moment, computed as
24423        \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 =
24424        1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$
24425        with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$.
24426        - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$.
24427        - \c 2: Least median of squares.
24428        - \c 3: Least trimmed of squares.
24429     **/
24430     double variance(const unsigned int variance_method=1) const {
24431       double foo;
24432       return variance_mean(variance_method,foo);
24433     }
24434 
24435     //! Return the variance as well as the average of the pixel values.
24436     /**
24437        \param variance_method Method used to estimate the variance (see variance(const unsigned int) const).
24438        \param[out] mean Average pixel value.
24439     **/
24440     template<typename t>
24441     double variance_mean(const unsigned int variance_method, t& mean) const {
24442       if (is_empty())
24443         throw CImgInstanceException(_cimg_instance
24444                                     "variance_mean(): Empty instance.",
24445                                     cimg_instance);
24446 
24447       double variance = 0, average = 0;
24448       const ulongT siz = size();
24449       switch (variance_method) {
24450       case 0 : { // Least mean square (standard definition)
24451         double S = 0, S2 = 0;
24452         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
24453         variance = (S2 - S*S/siz)/siz;
24454         average = S;
24455       } break;
24456       case 1 : { // Least mean square (robust definition)
24457         double S = 0, S2 = 0;
24458         cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
24459         variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
24460         average = S;
24461       } break;
24462       case 2 : { // Least Median of Squares (MAD)
24463         CImg<Tfloat> buf(*this,false);
24464         buf.sort();
24465         const ulongT siz2 = siz>>1;
24466         const double med_i = (double)buf[siz2];
24467         cimg_for(buf,ptrs,Tfloat) {
24468           const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val;
24469         }
24470         buf.sort();
24471         const double sig = (double)(1.4828*buf[siz2]);
24472         variance = sig*sig;
24473       } break;
24474       default : { // Least trimmed of Squares
24475         CImg<Tfloat> buf(*this,false);
24476         const ulongT siz2 = siz>>1;
24477         cimg_for(buf,ptrs,Tfloat) {
24478           const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val;
24479         }
24480         buf.sort();
24481         double a = 0;
24482         const Tfloat *ptrs = buf._data;
24483         for (ulongT j = 0; j<siz2; ++j) a+=(double)*(ptrs++);
24484         const double sig = (double)(2.6477*std::sqrt(a/siz2));
24485         variance = sig*sig;
24486       }
24487       }
24488       mean = (t)(average/siz);
24489       return variance>0?variance:0;
24490     }
24491 
24492     //! Return estimated variance of the noise.
24493     /**
24494        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
24495        \note Because of structures such as edges in images it is
24496        recommanded to use a robust variance estimation. The variance of the
24497        noise is estimated by computing the variance of the Laplacian \f$(\Delta
24498        I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
24499        \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
24500     **/
24501     double variance_noise(const unsigned int variance_method=2) const {
24502       if (is_empty())
24503         throw CImgInstanceException(_cimg_instance
24504                                     "variance_noise(): Empty instance.",
24505                                     cimg_instance);
24506 
24507       const ulongT siz = size();
24508       if (!siz || !_data) return 0;
24509       if (variance_method>1) { // Compute a scaled version of the Laplacian.
24510         CImg<Tdouble> tmp(*this,false);
24511         if (_depth==1) {
24512           const double cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed.
24513           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=262144 && _spectrum>=2))
24514           cimg_forC(*this,c) {
24515             CImg_3x3(I,T);
24516             cimg_for3x3(*this,x,y,0,c,I,T) {
24517               tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn +
24518                                  (double)Icp - 4*(double)Icc);
24519             }
24520           }
24521         } else {
24522           const double cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed.
24523           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=262144 && _spectrum>=2))
24524           cimg_forC(*this,c) {
24525             CImg_3x3x3(I,T);
24526             cimg_for3x3x3(*this,x,y,z,c,I,T) {
24527               tmp(x,y,z,c) = cste*(
24528                                    (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc +
24529                                    (double)Iccn + (double)Iccp - 6*(double)Iccc);
24530             }
24531           }
24532         }
24533         return tmp.variance(variance_method);
24534       }
24535 
24536       // Version that doesn't need intermediate images.
24537       double variance = 0, S = 0, S2 = 0;
24538       if (_depth==1) {
24539         const double cste = 1.0/std::sqrt(20.0);
24540         CImg_3x3(I,T);
24541         cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
24542           const double val = cste*((double)Inc + (double)Ipc +
24543                                    (double)Icn + (double)Icp - 4*(double)Icc);
24544           S+=val; S2+=val*val;
24545         }
24546       } else {
24547         const double cste = 1.0/std::sqrt(42.0);
24548         CImg_3x3x3(I,T);
24549         cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
24550           const double val = cste *
24551             ((double)Incc + (double)Ipcc + (double)Icnc +
24552              (double)Icpc +
24553              (double)Iccn + (double)Iccp - 6*(double)Iccc);
24554           S+=val; S2+=val*val;
24555         }
24556       }
24557       if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
24558       else variance = (S2 - S*S/siz)/siz;
24559       return variance>0?variance:0;
24560     }
24561 
24562     //! Compute the MSE (Mean-Squared Error) between two images.
24563     /**
24564        \param img Image used as the second argument of the MSE operator.
24565     **/
24566     template<typename t>
24567     double MSE(const CImg<t>& img) const {
24568       if (img.size()!=size())
24569         throw CImgArgumentException(_cimg_instance
24570                                     "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
24571                                     cimg_instance,
24572                                     img._width,img._height,img._depth,img._spectrum,img._data);
24573       double vMSE = 0;
24574       const t* ptr2 = img._data;
24575       cimg_for(*this,ptr1,T) {
24576         const double diff = (double)*ptr1 - (double)*(ptr2++);
24577         vMSE+=diff*diff;
24578       }
24579       const ulongT siz = img.size();
24580       if (siz) vMSE/=siz;
24581       return vMSE;
24582     }
24583 
24584     //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
24585     /**
24586        \param img Image used as the second argument of the PSNR operator.
24587        \param max_value Maximum theoretical value of the signal.
24588      **/
24589     template<typename t>
24590     double PSNR(const CImg<t>& img, const double max_value=255) const {
24591       const double vMSE = (double)std::sqrt(MSE(img));
24592       return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max());
24593     }
24594 
24595     //! Evaluate math formula.
24596     /**
24597        \param expression Math formula, as a C-string.
24598        \param x Value of the pre-defined variable \c x.
24599        \param y Value of the pre-defined variable \c y.
24600        \param z Value of the pre-defined variable \c z.
24601        \param c Value of the pre-defined variable \c c.
24602        \param list_inputs A list of input images attached to the specified math formula.
24603        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
24604     **/
24605     double eval(const char *const expression,
24606                 const double x=0, const double y=0, const double z=0, const double c=0,
24607                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
24608       return _eval(this,expression,x,y,z,c,list_inputs,list_outputs);
24609     }
24610 
24611     //! Evaluate math formula \const.
24612     double eval(const char *const expression,
24613                 const double x=0, const double y=0, const double z=0, const double c=0,
24614                 const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
24615       return _eval(0,expression,x,y,z,c,list_inputs,list_outputs);
24616     }
24617 
24618     double _eval(CImg<T> *const img_output, const char *const expression,
24619                  const double x, const double y, const double z, const double c,
24620                  const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
24621       if (!expression || !*expression) return 0;
24622       if (!expression[1]) switch (*expression) { // Single-char optimization.
24623         case 'w' : return (double)_width;
24624         case 'h' : return (double)_height;
24625         case 'd' : return (double)_depth;
24626         case 's' : return (double)_spectrum;
24627         case 'r' : return (double)_is_shared;
24628         }
24629       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
24630                                          *expression=='*' || *expression==':'),"eval",
24631                            *this,img_output,list_inputs,list_outputs,false);
24632       const double val = mp(x,y,z,c);
24633       mp.end();
24634       return val;
24635     }
24636 
24637     //! Evaluate math formula.
24638     /**
24639        \param[out] output Contains values of output vector returned by the evaluated expression
24640          (or is empty if the returned type is scalar).
24641        \param expression Math formula, as a C-string.
24642        \param x Value of the pre-defined variable \c x.
24643        \param y Value of the pre-defined variable \c y.
24644        \param z Value of the pre-defined variable \c z.
24645        \param c Value of the pre-defined variable \c c.
24646        \param list_inputs A list of input images attached to the specified math formula.
24647        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
24648     **/
24649     template<typename t>
24650     void eval(CImg<t> &output, const char *const expression,
24651               const double x=0, const double y=0, const double z=0, const double c=0,
24652               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
24653       _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs);
24654     }
24655 
24656     //! Evaluate math formula \const.
24657     template<typename t>
24658     void eval(CImg<t>& output, const char *const expression,
24659               const double x=0, const double y=0, const double z=0, const double c=0,
24660               const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
24661       _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs);
24662     }
24663 
24664     template<typename t>
24665     void _eval(CImg<t>& output, CImg<T> *const img_output, const char *const expression,
24666                const double x, const double y, const double z, const double c,
24667                const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs) const {
24668       if (!expression || !*expression) { output.assign(1); *output = 0; }
24669       if (!expression[1]) switch (*expression) { // Single-char optimization.
24670         case 'w' : output.assign(1); *output = (t)_width; break;
24671         case 'h' : output.assign(1); *output = (t)_height; break;
24672         case 'd' : output.assign(1); *output = (t)_depth; break;
24673         case 's' : output.assign(1); *output = (t)_spectrum; break;
24674         case 'r' : output.assign(1); *output = (t)_is_shared; break;
24675         }
24676       _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
24677                                          *expression=='*' || *expression==':'),"eval",
24678                            *this,img_output,list_inputs,list_outputs,false);
24679       output.assign(1,std::max(1U,mp.result_dim));
24680       mp(x,y,z,c,output._data);
24681       mp.end();
24682     }
24683 
24684     //! Evaluate math formula on a set of variables.
24685     /**
24686        \param expression Math formula, as a C-string.
24687        \param xyzc Set of values (x,y,z,c) used for the evaluation.
24688        \param list_inputs A list of input images attached to the specified math formula.
24689        \param[out] list_outputs A pointer to a list of output images attached to the specified math formula.
24690     **/
24691     template<typename t>
24692     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
24693                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
24694       return _eval(this,expression,xyzc,list_inputs,list_outputs);
24695     }
24696 
24697     //! Evaluate math formula on a set of variables \const.
24698     template<typename t>
24699     CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
24700                        const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
24701       return _eval(0,expression,xyzc,list_inputs,list_outputs);
24702     }
24703 
24704     template<typename t>
24705     CImg<doubleT> _eval(CImg<T> *const output, const char *const expression, const CImg<t>& xyzc,
24706                         const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
24707       CImg<doubleT> res(1,xyzc.size()/4);
24708       if (!expression || !*expression) return res.fill(0);
24709       _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false);
24710 #ifdef cimg_use_openmp
24711       cimg_pragma_openmp(parallel if (res._height>=512))
24712       {
24713         _cimg_math_parser
24714           _mp = omp_get_thread_num()?mp:_cimg_math_parser(),
24715           &lmp = omp_get_thread_num()?_mp:mp;
24716         cimg_pragma_openmp(for)
24717           for (unsigned int i = 0; i<res._height; ++i) {
24718             const unsigned int i4 = 4*i;
24719             const double
24720               x = (double)xyzc[i4], y = (double)xyzc[i4 + 1],
24721               z = (double)xyzc[i4 + 2], c = (double)xyzc[i4 + 3];
24722             res[i] = lmp(x,y,z,c);
24723           }
24724         }
24725 #else
24726       const t *ps = xyzc._data;
24727       cimg_for(res,pd,double) {
24728         const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++);
24729         *pd = mp(x,y,z,c);
24730       }
24731 #endif
24732       mp.end();
24733       return res;
24734     }
24735 
24736     //! Compute statistics vector from the pixel values.
24737     /*
24738        \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
24739        \return Statistics vector as
24740          <tt>[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]</tt>.
24741     **/
24742     CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
24743       if (is_empty()) return CImg<doubleT>();
24744       const ulongT siz = size();
24745       const longT off_end = (longT)siz;
24746       double S = 0, S2 = 0, P = 1;
24747       longT offm = 0, offM = 0;
24748       T m = *_data, M = m;
24749 
24750       cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if(siz>=131072)) {
24751         longT loffm = 0, loffM = 0;
24752         T lm = *_data, lM = lm;
24753         cimg_pragma_openmp(for)
24754         for (longT off = 0; off<off_end; ++off) {
24755           const T val = _data[off];
24756           const double _val = (double)val;
24757           if (val<lm) { lm = val; loffm = off; }
24758           if (val>lM) { lM = val; loffM = off; }
24759           S+=_val;
24760           S2+=_val*_val;
24761           P*=_val;
24762         }
24763         cimg_pragma_openmp(critical(get_stats)) {
24764           if (lm<m || (lm==m && loffm<offm)) { m = lm; offm = loffm; }
24765           if (lM>M || (lM==M && loffM<offM)) { M = lM; offM = loffM; }
24766         }
24767       }
24768 
24769       const double
24770         mean_value = S/siz,
24771         _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
24772         (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
24773          variance(variance_method)),
24774         variance_value = _variance_value>0?_variance_value:0;
24775       int
24776         xm = 0, ym = 0, zm = 0, cm = 0,
24777         xM = 0, yM = 0, zM = 0, cM = 0;
24778       contains(_data[offm],xm,ym,zm,cm);
24779       contains(_data[offM],xM,yM,zM,cM);
24780       return CImg<Tdouble>(1,14).fill((double)m,(double)M,mean_value,variance_value,
24781                                       (double)xm,(double)ym,(double)zm,(double)cm,
24782                                       (double)xM,(double)yM,(double)zM,(double)cM,
24783                                       S,P);
24784     }
24785 
24786     //! Compute statistics vector from the pixel values \inplace.
24787     CImg<T>& stats(const unsigned int variance_method=1) {
24788       return get_stats(variance_method).move_to(*this);
24789     }
24790 
24791     //@}
24792     //-------------------------------------
24793     //
24794     //! \name Vector / Matrix Operations
24795     //@{
24796     //-------------------------------------
24797 
24798     //! Compute norm of the image, viewed as a matrix.
24799     /**
24800        \param magnitude_type Norm type. Can be:
24801        - \c -1: Linf-norm
24802        - \c 0: L0-norm
24803        - \c 1: L1-norm
24804        - \c 2: L2-norm
24805     **/
24806     double magnitude(const int magnitude_type=2) const {
24807       if (is_empty())
24808         throw CImgInstanceException(_cimg_instance
24809                                     "magnitude(): Empty instance.",
24810                                     cimg_instance);
24811       double res = 0;
24812       switch (magnitude_type) {
24813       case -1 : {
24814         cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
24815       } break;
24816       case 1 : {
24817         cimg_for(*this,ptrs,T) res+=(double)cimg::abs(*ptrs);
24818       } break;
24819       default : {
24820         cimg_for(*this,ptrs,T) res+=(double)cimg::sqr(*ptrs);
24821         res = (double)std::sqrt(res);
24822       }
24823       }
24824       return res;
24825     }
24826 
24827     //! Compute the trace of the image, viewed as a matrix.
24828     /**
24829      **/
24830     double trace() const {
24831       if (is_empty())
24832         throw CImgInstanceException(_cimg_instance
24833                                     "trace(): Empty instance.",
24834                                     cimg_instance);
24835       double res = 0;
24836       cimg_forX(*this,k) res+=(double)(*this)(k,k);
24837       return res;
24838     }
24839 
24840     //! Compute the determinant of the image, viewed as a matrix.
24841     /**
24842      **/
24843     double det() const {
24844       if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
24845         throw CImgInstanceException(_cimg_instance
24846                                     "det(): Instance is not a square matrix.",
24847                                     cimg_instance);
24848 
24849       switch (_width) {
24850       case 1 : return (double)((*this)(0,0));
24851       case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0));
24852       case 3 : {
24853         const double
24854           a = (double)_data[0], d = (double)_data[1], g = (double)_data[2],
24855           b = (double)_data[3], e = (double)_data[4], h = (double)_data[5],
24856           c = (double)_data[6], f = (double)_data[7], i = (double)_data[8];
24857         return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
24858       }
24859       default : {
24860         CImg<Tfloat> lu(*this,false);
24861         CImg<uintT> indx;
24862         bool d;
24863         lu._LU(indx,d);
24864         double res = d?(double)1:(double)-1;
24865         cimg_forX(lu,i) res*=lu(i,i);
24866         return res;
24867       }
24868       }
24869     }
24870 
24871     //! Compute the dot product between instance and argument, viewed as matrices.
24872     /**
24873        \param img Image used as a second argument of the dot product.
24874     **/
24875     template<typename t>
24876     double dot(const CImg<t>& img) const {
24877       if (is_empty())
24878         throw CImgInstanceException(_cimg_instance
24879                                     "dot(): Empty instance.",
24880                                     cimg_instance);
24881       if (!img)
24882         throw CImgArgumentException(_cimg_instance
24883                                     "dot(): Empty specified image.",
24884                                     cimg_instance);
24885 
24886       const ulongT nb = std::min(size(),img.size());
24887       double res = 0;
24888       for (ulongT off = 0; off<nb; ++off) res+=(double)_data[off]*(double)img[off];
24889       return res;
24890     }
24891 
24892     //! Get vector-valued pixel located at specified position.
24893     /**
24894        \param x X-coordinate of the pixel value.
24895        \param y Y-coordinate of the pixel value.
24896        \param z Z-coordinate of the pixel value.
24897     **/
24898     CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
24899       CImg<T> res;
24900       if (res._height!=_spectrum) res.assign(1,_spectrum);
24901       const ulongT whd = (ulongT)_width*_height*_depth;
24902       const T *ptrs = data(x,y,z);
24903       T *ptrd = res._data;
24904       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
24905       return res;
24906     }
24907 
24908     //! Get (square) matrix-valued pixel located at specified position.
24909     /**
24910        \param x X-coordinate of the pixel value.
24911        \param y Y-coordinate of the pixel value.
24912        \param z Z-coordinate of the pixel value.
24913        \note - The spectrum() of the image must be a square.
24914      **/
24915     CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
24916       const int n = (int)cimg::round(std::sqrt((double)_spectrum));
24917       const T *ptrs = data(x,y,z,0);
24918       const ulongT whd = (ulongT)_width*_height*_depth;
24919       CImg<T> res(n,n);
24920       T *ptrd = res._data;
24921       cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
24922       return res;
24923     }
24924 
24925     //! Get tensor-valued pixel located at specified position.
24926     /**
24927        \param x X-coordinate of the pixel value.
24928        \param y Y-coordinate of the pixel value.
24929        \param z Z-coordinate of the pixel value.
24930     **/
24931     CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
24932       const T *ptrs = data(x,y,z,0);
24933       const ulongT whd = (ulongT)_width*_height*_depth;
24934       if (_spectrum==6)
24935         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd));
24936       if (_spectrum==3)
24937         return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd));
24938       return tensor(*ptrs);
24939     }
24940 
24941     //! Set vector-valued pixel at specified position.
24942     /**
24943        \param vec Vector to put on the instance image.
24944        \param x X-coordinate of the pixel value.
24945        \param y Y-coordinate of the pixel value.
24946        \param z Z-coordinate of the pixel value.
24947     **/
24948     template<typename t>
24949     CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
24950       if (x<_width && y<_height && z<_depth) {
24951         const t *ptrs = vec._data;
24952         const ulongT whd = (ulongT)_width*_height*_depth;
24953         T *ptrd = data(x,y,z);
24954         for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) {
24955           *ptrd = (T)*(ptrs++); ptrd+=whd;
24956         }
24957       }
24958       return *this;
24959     }
24960 
24961     //! Set (square) matrix-valued pixel at specified position.
24962     /**
24963        \param mat Matrix to put on the instance image.
24964        \param x X-coordinate of the pixel value.
24965        \param y Y-coordinate of the pixel value.
24966        \param z Z-coordinate of the pixel value.
24967     **/
24968     template<typename t>
24969     CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
24970       return set_vector_at(mat,x,y,z);
24971     }
24972 
24973     //! Set tensor-valued pixel at specified position.
24974     /**
24975        \param ten Tensor to put on the instance image.
24976        \param x X-coordinate of the pixel value.
24977        \param y Y-coordinate of the pixel value.
24978        \param z Z-coordinate of the pixel value.
24979     **/
24980     template<typename t>
24981     CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
24982       T *ptrd = data(x,y,z,0);
24983       const ulongT siz = (ulongT)_width*_height*_depth;
24984       if (ten._height==2) {
24985         *ptrd = (T)ten[0]; ptrd+=siz;
24986         *ptrd = (T)ten[1]; ptrd+=siz;
24987         *ptrd = (T)ten[3];
24988       }
24989       else {
24990         *ptrd = (T)ten[0]; ptrd+=siz;
24991         *ptrd = (T)ten[1]; ptrd+=siz;
24992         *ptrd = (T)ten[2]; ptrd+=siz;
24993         *ptrd = (T)ten[4]; ptrd+=siz;
24994         *ptrd = (T)ten[5]; ptrd+=siz;
24995         *ptrd = (T)ten[8];
24996       }
24997       return *this;
24998     }
24999 
25000     //! Unroll pixel values along axis \c y.
25001     /**
25002        \note Equivalent to \code unroll('y'); \endcode.
25003     **/
25004     CImg<T>& vector() {
25005       return unroll('y');
25006     }
25007 
25008     //! Unroll pixel values along axis \c y \newinstance.
25009     CImg<T> get_vector() const {
25010       return get_unroll('y');
25011     }
25012 
25013     //! Resize image to become a scalar square matrix.
25014     /**
25015      **/
25016     CImg<T>& matrix() {
25017       const ulongT siz = size();
25018       switch (siz) {
25019       case 1 : break;
25020       case 4 : _width = _height = 2; break;
25021       case 9 : _width = _height = 3; break;
25022       case 16 : _width = _height = 4; break;
25023       case 25 : _width = _height = 5; break;
25024       case 36 : _width = _height = 6; break;
25025       case 49 : _width = _height = 7; break;
25026       case 64 : _width = _height = 8; break;
25027       case 81 : _width = _height = 9; break;
25028       case 100 : _width = _height = 10; break;
25029       default : {
25030         ulongT i = 11, i2 = i*i;
25031         while (i2<siz) { i2+=2*i + 1; ++i; }
25032         if (i2==siz) _width = _height = i;
25033         else throw CImgInstanceException(_cimg_instance
25034                                          "matrix(): Invalid instance size %u (should be a square integer).",
25035                                          cimg_instance,
25036                                          siz);
25037       }
25038       }
25039       return *this;
25040     }
25041 
25042     //! Resize image to become a scalar square matrix \newinstance.
25043     CImg<T> get_matrix() const {
25044       return (+*this).matrix();
25045     }
25046 
25047     //! Resize image to become a symmetric tensor.
25048     /**
25049      **/
25050     CImg<T>& tensor() {
25051       return get_tensor().move_to(*this);
25052     }
25053 
25054     //! Resize image to become a symmetric tensor \newinstance.
25055     CImg<T> get_tensor() const {
25056       CImg<T> res;
25057       const ulongT siz = size();
25058       switch (siz) {
25059       case 1 : break;
25060       case 3 :
25061         res.assign(2,2);
25062         res(0,0) = (*this)(0);
25063         res(1,0) = res(0,1) = (*this)(1);
25064         res(1,1) = (*this)(2);
25065         break;
25066       case 6 :
25067         res.assign(3,3);
25068         res(0,0) = (*this)(0);
25069         res(1,0) = res(0,1) = (*this)(1);
25070         res(2,0) = res(0,2) = (*this)(2);
25071         res(1,1) = (*this)(3);
25072         res(2,1) = res(1,2) = (*this)(4);
25073         res(2,2) = (*this)(5);
25074         break;
25075       default :
25076         throw CImgInstanceException(_cimg_instance
25077                                     "tensor(): Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).",
25078                                     cimg_instance);
25079       }
25080       return res;
25081     }
25082 
25083     //! Resize image to become a diagonal matrix.
25084     /**
25085        \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient.
25086     **/
25087     CImg<T>& diagonal() {
25088       return get_diagonal().move_to(*this);
25089     }
25090 
25091     //! Resize image to become a diagonal matrix \newinstance.
25092     CImg<T> get_diagonal() const {
25093       if (is_empty()) return *this;
25094       const unsigned int siz = (unsigned int)size();
25095       CImg<T> res(siz,siz,1,1,0);
25096       cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off];
25097       return res;
25098     }
25099 
25100     //! Replace the image by an identity matrix.
25101     /**
25102        \note If the instance image is not square, it is resized to a square matrix using its maximum
25103        dimension as a reference.
25104     **/
25105     CImg<T>& identity_matrix() {
25106       return identity_matrix(std::max(_width,_height)).move_to(*this);
25107     }
25108 
25109     //! Replace the image by an identity matrix \newinstance.
25110     CImg<T> get_identity_matrix() const {
25111       return identity_matrix(std::max(_width,_height));
25112     }
25113 
25114     //! Fill image with a linear sequence of values.
25115     /**
25116        \param a0 Starting value of the sequence.
25117        \param a1 Ending value of the sequence.
25118     **/
25119     CImg<T>& sequence(const T& a0, const T& a1) {
25120       if (is_empty()) return *this;
25121       const ulongT siz = size() - 1;
25122       T* ptr = _data;
25123       if (siz) {
25124         const double delta = (double)a1 - (double)a0;
25125         cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
25126       } else *ptr = a0;
25127       return *this;
25128     }
25129 
25130     //! Fill image with a linear sequence of values \newinstance.
25131     CImg<T> get_sequence(const T& a0, const T& a1) const {
25132       return (+*this).sequence(a0,a1);
25133     }
25134 
25135     //! Transpose the image, viewed as a matrix.
25136     /**
25137        \note Equivalent to \code permute_axes("yxzc"); \endcode
25138     **/
25139     CImg<T>& transpose() {
25140       if (_width==1) { _width = _height; _height = 1; return *this; }
25141       if (_height==1) { _height = _width; _width = 1; return *this; }
25142       if (_width==_height) {
25143         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));
25144         return *this;
25145       }
25146       return get_transpose().move_to(*this);
25147     }
25148 
25149     //! Transpose the image, viewed as a matrix \newinstance.
25150     CImg<T> get_transpose() const {
25151       return get_permute_axes("yxzc");
25152     }
25153 
25154     //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors.
25155     /**
25156        \param img Image used as the second argument of the cross product.
25157        \note The first argument of the cross product is \c *this.
25158      **/
25159     template<typename t>
25160     CImg<T>& cross(const CImg<t>& img) {
25161       if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
25162         throw CImgInstanceException(_cimg_instance
25163                                     "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3d vectors.",
25164                                     cimg_instance,
25165                                     img._width,img._height,img._depth,img._spectrum,img._data);
25166 
25167       const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
25168       (*this)[0] = (T)(y*img[2] - z*img[1]);
25169       (*this)[1] = (T)(z*img[0] - x*img[2]);
25170       (*this)[2] = (T)(x*img[1] - y*img[0]);
25171       return *this;
25172     }
25173 
25174     //! Compute the cross product between two \c 1x3 images, viewed as 3d vectors \newinstance.
25175     template<typename t>
25176     CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
25177       return CImg<_cimg_Tt>(*this).cross(img);
25178     }
25179 
25180     //! Invert the instance image, viewed as a matrix.
25181     /**
25182        \param use_LU Choose the inverting algorithm. Can be:
25183        - \c true: LU-based matrix inversion.
25184        - \c false: SVD-based matrix inversion.
25185     **/
25186     CImg<T>& invert(const bool use_LU=true) {
25187       if (_width!=_height || _depth!=1 || _spectrum!=1)
25188         throw CImgInstanceException(_cimg_instance
25189                                     "invert(): Instance is not a square matrix.",
25190                                     cimg_instance);
25191 #ifdef cimg_use_lapack
25192       int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
25193       Tfloat
25194         *const lapA = new Tfloat[N*N],
25195         *const WORK = new Tfloat[LWORK];
25196       cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
25197       cimg::getrf(N,lapA,IPIV,INFO);
25198       if (INFO)
25199         cimg::warn(_cimg_instance
25200                    "invert(): LAPACK function dgetrf_() returned error code %d.",
25201                    cimg_instance,
25202                    INFO);
25203       else {
25204         cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
25205         if (INFO)
25206           cimg::warn(_cimg_instance
25207                      "invert(): LAPACK function dgetri_() returned error code %d.",
25208                      cimg_instance,
25209                      INFO);
25210       }
25211       if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0);
25212       delete[] IPIV; delete[] lapA; delete[] WORK;
25213 #else
25214       const double dete = _width>3?-1.0:det();
25215       if (dete!=0.0 && _width==2) {
25216         const double
25217           a = _data[0], c = _data[1],
25218           b = _data[2], d = _data[3];
25219         _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
25220         _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
25221       } else if (dete!=0.0 && _width==3) {
25222         const double
25223           a = _data[0], d = _data[1], g = _data[2],
25224           b = _data[3], e = _data[4], h = _data[5],
25225           c = _data[6], f = _data[7], i = _data[8];
25226         _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete);
25227         _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete);
25228         _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete);
25229       } else {
25230         if (use_LU) { // LU-based inverse computation
25231           CImg<Tfloat> A(*this,false), indx, col(1,_width);
25232           bool d;
25233           A._LU(indx,d);
25234           cimg_forX(*this,j) {
25235             col.fill(0);
25236             col(j) = 1;
25237             col._solve(A,indx);
25238             cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
25239           }
25240         } else { // SVD-based inverse computation
25241           CImg<Tfloat> U(_width,_width), S(1,_width), V(_width,_width);
25242           SVD(U,S,V,false);
25243           U.transpose();
25244           cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k];
25245           S.diagonal();
25246           *this = V*S*U;
25247         }
25248       }
25249 #endif
25250       return *this;
25251     }
25252 
25253     //! Invert the instance image, viewed as a matrix \newinstance.
25254     CImg<Tfloat> get_invert(const bool use_LU=true) const {
25255       return CImg<Tfloat>(*this,false).invert(use_LU);
25256     }
25257 
25258     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix.
25259     /**
25260     **/
25261     CImg<T>& pseudoinvert() {
25262       return get_pseudoinvert().move_to(*this);
25263     }
25264 
25265     //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance.
25266     CImg<Tfloat> get_pseudoinvert() const {
25267       CImg<Tfloat> U, S, V;
25268       SVD(U,S,V);
25269       const Tfloat tolerance = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max();
25270       cimg_forX(V,x) {
25271 	const Tfloat s = S(x), invs = s>tolerance?1/s:0;
25272 	cimg_forY(V,y) V(x,y)*=invs;
25273       }
25274       return V*U.transpose();
25275     }
25276 
25277     //! Solve a system of linear equations.
25278     /**
25279        \param A Matrix of the linear system.
25280        \note Solve \c AX=B where \c B=*this.
25281     **/
25282     template<typename t>
25283     CImg<T>& solve(const CImg<t>& A) {
25284       if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
25285         throw CImgArgumentException(_cimg_instance
25286                                     "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have "
25287                                     "incompatible dimensions.",
25288                                     cimg_instance,
25289                                     A._width,A._height,A._depth,A._spectrum,A._data);
25290       typedef _cimg_Ttfloat Ttfloat;
25291       if (A._width==A._height) { // Classical linear system
25292         if (_width!=1) {
25293           CImg<T> res(_width,A._width);
25294           cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A));
25295           return res.move_to(*this);
25296         }
25297 #ifdef cimg_use_lapack
25298         char TRANS = 'N';
25299         int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
25300         Ttfloat
25301           *const lapA = new Ttfloat[N*N],
25302           *const lapB = new Ttfloat[N],
25303           *const WORK = new Ttfloat[LWORK];
25304         cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l));
25305         cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i));
25306         cimg::getrf(N,lapA,IPIV,INFO);
25307         if (INFO)
25308           cimg::warn(_cimg_instance
25309                      "solve(): LAPACK library function dgetrf_() returned error code %d.",
25310                      cimg_instance,
25311                      INFO);
25312 
25313         if (!INFO) {
25314           cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
25315           if (INFO)
25316             cimg::warn(_cimg_instance
25317                        "solve(): LAPACK library function dgetrs_() returned error code %d.",
25318                        cimg_instance,
25319                        INFO);
25320         }
25321         if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0);
25322         delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
25323 #else
25324         CImg<Ttfloat> lu(A,false);
25325         CImg<Ttfloat> indx;
25326         bool d;
25327         lu._LU(indx,d);
25328         _solve(lu,indx);
25329 #endif
25330       } else { // Least-square solution for non-square systems.
25331 #ifdef cimg_use_lapack
25332         if (_width!=1) {
25333           CImg<T> res(_width,A._width);
25334           cimg_forX(*this,i) res.draw_image(i,get_column(i).solve(A));
25335           return res.move_to(*this);
25336         }
25337 	char TRANS = 'N';
25338         int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
25339 	Ttfloat WORK_QUERY;
25340 	Ttfloat
25341 	  * const lapA = new Ttfloat[M*N],
25342 	  * const lapB = new Ttfloat[M*NRHS];
25343 	cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
25344 	LWORK = (int) WORK_QUERY;
25345 	Ttfloat *const WORK = new Ttfloat[LWORK];
25346         cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l));
25347         cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l));
25348 	cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
25349         if (INFO != 0)
25350           cimg::warn(_cimg_instance
25351                      "solve(): LAPACK library function sgels() returned error code %d.",
25352                      cimg_instance,
25353                      INFO);
25354 	assign(NRHS, N);
25355         if (!INFO)
25356           cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l];
25357         else
25358           assign(A.get_pseudoinvert()*(*this));
25359         delete[] lapA; delete[] lapB; delete[] WORK;
25360 #else
25361 	assign(A.get_pseudoinvert()*(*this));
25362 #endif
25363       }
25364       return *this;
25365     }
25366 
25367     //! Solve a system of linear equations \newinstance.
25368     template<typename t>
25369     CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A) const {
25370       return CImg<_cimg_Ttfloat>(*this,false).solve(A);
25371     }
25372 
25373     template<typename t, typename ti>
25374     CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
25375       typedef _cimg_Ttfloat Ttfloat;
25376       const int N = (int)size();
25377       int ii = -1;
25378       Ttfloat sum;
25379       for (int i = 0; i<N; ++i) {
25380         const int ip = (int)indx[i];
25381         Ttfloat sum = (*this)(ip);
25382         (*this)(ip) = (*this)(i);
25383         if (ii>=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j);
25384         else if (sum!=0) ii = i;
25385         (*this)(i) = (T)sum;
25386       }
25387       for (int i = N - 1; i>=0; --i) {
25388         sum = (*this)(i);
25389         for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
25390         (*this)(i) = (T)(sum/A(i,i));
25391       }
25392       return *this;
25393     }
25394 
25395     //! Solve a tridiagonal system of linear equations.
25396     /**
25397        \param A Coefficients of the tridiagonal system.
25398        A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
25399        stored as a 3 columns matrix
25400        \note Solve AX=B where \c B=*this, using the Thomas algorithm.
25401     **/
25402     template<typename t>
25403     CImg<T>& solve_tridiagonal(const CImg<t>& A) {
25404       const unsigned int siz = (unsigned int)size();
25405       if (A._width!=3 || A._height!=siz)
25406         throw CImgArgumentException(_cimg_instance
25407                                     "solve_tridiagonal(): Instance and tridiagonal matrix "
25408                                     "(%u,%u,%u,%u,%p) have incompatible dimensions.",
25409                                     cimg_instance,
25410                                     A._width,A._height,A._depth,A._spectrum,A._data);
25411       typedef _cimg_Ttfloat Ttfloat;
25412       const Ttfloat epsilon = 1e-4f;
25413       CImg<Ttfloat> B = A.get_column(1), V(*this,false);
25414       for (int i = 1; i<(int)siz; ++i) {
25415         const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon);
25416         B[i] -= m*A(2,i - 1);
25417         V[i] -= m*V[i - 1];
25418       }
25419       (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon));
25420       for (int i = (int)siz - 2; i>=0; --i) (*this)[i] = (T)((V[i] - A(2,i)*(*this)[i + 1])/(B[i]?B[i]:epsilon));
25421       return *this;
25422     }
25423 
25424     //! Solve a tridiagonal system of linear equations \newinstance.
25425     template<typename t>
25426     CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
25427       return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
25428     }
25429 
25430     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
25431     /**
25432        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
25433        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
25434     **/
25435     template<typename t>
25436     const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
25437       if (is_empty()) { val.assign(); vec.assign(); }
25438       else {
25439         if (_width!=_height || _depth>1 || _spectrum>1)
25440           throw CImgInstanceException(_cimg_instance
25441                                       "eigen(): Instance is not a square matrix.",
25442                                       cimg_instance);
25443 
25444         if (val.size()<(ulongT)_width) val.assign(1,_width);
25445         if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width);
25446         switch (_width) {
25447         case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
25448         case 2 : {
25449           const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
25450           double f = e*e - 4*(a*d - b*c);
25451           if (f<0)
25452             cimg::warn(_cimg_instance
25453                        "eigen(): Complex eigenvalues found.",
25454                        cimg_instance);
25455 
25456           f = std::sqrt(f);
25457           const double
25458             l1 = 0.5*(e - f),
25459             l2 = 0.5*(e + f),
25460             b2 = b*b,
25461             norm1 = std::sqrt(cimg::sqr(l2 - a) + b2),
25462             norm2 = std::sqrt(cimg::sqr(l1 - a) + b2);
25463           val[0] = (t)l2;
25464           val[1] = (t)l1;
25465           if (norm1>0) { vec(0,0) = (t)(b/norm1); vec(0,1) = (t)((l2 - a)/norm1); } else { vec(0,0) = 1; vec(0,1) = 0; }
25466           if (norm2>0) { vec(1,0) = (t)(b/norm2); vec(1,1) = (t)((l1 - a)/norm2); } else { vec(1,0) = 1; vec(1,1) = 0; }
25467         } break;
25468         default :
25469           throw CImgInstanceException(_cimg_instance
25470                                       "eigen(): Eigenvalues computation of general matrices is limited "
25471                                       "to 2x2 matrices.",
25472                                       cimg_instance);
25473         }
25474       }
25475       return *this;
25476     }
25477 
25478     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
25479     /**
25480        \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const.
25481     **/
25482     CImgList<Tfloat> get_eigen() const {
25483       CImgList<Tfloat> res(2);
25484       eigen(res[0],res[1]);
25485       return res;
25486     }
25487 
25488     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
25489     /**
25490        \param[out] val Vector of the estimated eigenvalues, in decreasing order.
25491        \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
25492     **/
25493     template<typename t>
25494     const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
25495       if (is_empty()) { val.assign(); vec.assign(); }
25496       else {
25497 #ifdef cimg_use_lapack
25498         char JOB = 'V', UPLO = 'U';
25499         int N = _width, LWORK = 4*N, INFO;
25500         Tfloat
25501           *const lapA = new Tfloat[N*N],
25502           *const lapW = new Tfloat[N],
25503           *const WORK = new Tfloat[LWORK];
25504         cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
25505         cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
25506         if (INFO)
25507           cimg::warn(_cimg_instance
25508                      "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
25509                      cimg_instance,
25510                      INFO);
25511 
25512         val.assign(1,N);
25513 	vec.assign(N,N);
25514         if (!INFO) {
25515           cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i];
25516           cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]);
25517         } else { val.fill(0); vec.fill(0); }
25518         delete[] lapA; delete[] lapW; delete[] WORK;
25519 #else
25520         if (_width!=_height || _depth>1 || _spectrum>1)
25521           throw CImgInstanceException(_cimg_instance
25522                                       "eigen(): Instance is not a square matrix.",
25523                                       cimg_instance);
25524 
25525 	val.assign(1,_width);
25526 	if (vec._data) vec.assign(_width,_width);
25527         if (_width<3) {
25528           eigen(val,vec);
25529           if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices.
25530           return *this;
25531         }
25532         CImg<t> V(_width,_width);
25533         Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M));
25534         (CImg<Tfloat>(*this,false)/=maxabs).SVD(vec,val,V,false);
25535         if (maxabs!=1) val*=maxabs;
25536 
25537 	bool is_ambiguous = false;
25538 	float eig = 0;
25539 	cimg_forY(val,p) {       // check for ambiguous cases.
25540 	  if (val[p]>eig) eig = (float)val[p];
25541           t scal = 0;
25542           cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
25543           if (cimg::abs(scal)<0.9f) is_ambiguous = true;
25544           if (scal<0) val[p] = -val[p];
25545         }
25546 	if (is_ambiguous) {
25547 	  ++(eig*=2);
25548 	  SVD(vec,val,V,false,40,eig);
25549 	  val-=eig;
25550 	}
25551         CImg<intT> permutations;  // sort eigenvalues in decreasing order
25552         CImg<t> tmp(_width);
25553         val.sort(permutations,false);
25554         cimg_forY(vec,k) {
25555           cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
25556           std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
25557         }
25558 #endif
25559       }
25560       return *this;
25561     }
25562 
25563     //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
25564     /**
25565        \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in
25566          symmetric_eigen(CImg<t>&,CImg<t>&) const.
25567     **/
25568     CImgList<Tfloat> get_symmetric_eigen() const {
25569       CImgList<Tfloat> res(2);
25570       symmetric_eigen(res[0],res[1]);
25571       return res;
25572     }
25573 
25574     //! Sort pixel values and get sorting permutations.
25575     /**
25576        \param[out] permutations Permutation map used for the sorting.
25577        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
25578     **/
25579     template<typename t>
25580     CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
25581       permutations.assign(_width,_height,_depth,_spectrum);
25582       if (is_empty()) return *this;
25583       cimg_foroff(permutations,off) permutations[off] = (t)off;
25584       return _quicksort(0,size() - 1,permutations,is_increasing,true);
25585     }
25586 
25587     //! Sort pixel values and get sorting permutations \newinstance.
25588     template<typename t>
25589     CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
25590       return (+*this).sort(permutations,is_increasing);
25591     }
25592 
25593     //! Sort pixel values.
25594     /**
25595        \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
25596        \param axis Tells if the value sorting must be done along a specific axis. Can be:
25597        - \c 0: All pixel values are sorted, independently on their initial position.
25598        - \c 'x': Image columns are sorted, according to the first value in each column.
25599        - \c 'y': Image rows are sorted, according to the first value in each row.
25600        - \c 'z': Image slices are sorted, according to the first value in each slice.
25601        - \c 'c': Image channels are sorted, according to the first value in each channel.
25602     **/
25603     CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
25604       if (is_empty()) return *this;
25605       CImg<uintT> perm;
25606       switch (cimg::lowercase(axis)) {
25607       case 0 :
25608         _quicksort(0,size() - 1,perm,is_increasing,false);
25609         break;
25610       case 'x' : {
25611         perm.assign(_width);
25612         get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing);
25613         CImg<T> img(*this,false);
25614         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
25615       } break;
25616       case 'y' : {
25617         perm.assign(_height);
25618         get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing);
25619         CImg<T> img(*this,false);
25620         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
25621       } break;
25622       case 'z' : {
25623         perm.assign(_depth);
25624         get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing);
25625         CImg<T> img(*this,false);
25626         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
25627       } break;
25628       case 'c' : {
25629         perm.assign(_spectrum);
25630         get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing);
25631         CImg<T> img(*this,false);
25632         cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
25633       } break;
25634       default :
25635         throw CImgArgumentException(_cimg_instance
25636                                     "sort(): Invalid specified axis '%c' "
25637                                     "(should be { x | y | z | c }).",
25638                                     cimg_instance,axis);
25639       }
25640       return *this;
25641     }
25642 
25643     //! Sort pixel values \newinstance.
25644     CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
25645       return (+*this).sort(is_increasing,axis);
25646     }
25647 
25648     template<typename t>
25649     CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations,
25650                         const bool is_increasing, const bool is_permutations) {
25651       if (indm<indM) {
25652         const long mid = (indm + indM)/2;
25653         if (is_increasing) {
25654           if ((*this)[indm]>(*this)[mid]) {
25655             cimg::swap((*this)[indm],(*this)[mid]);
25656             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
25657           }
25658           if ((*this)[mid]>(*this)[indM]) {
25659             cimg::swap((*this)[indM],(*this)[mid]);
25660             if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
25661           }
25662           if ((*this)[indm]>(*this)[mid]) {
25663             cimg::swap((*this)[indm],(*this)[mid]);
25664             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
25665           }
25666         } else {
25667           if ((*this)[indm]<(*this)[mid]) {
25668             cimg::swap((*this)[indm],(*this)[mid]);
25669             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
25670           }
25671           if ((*this)[mid]<(*this)[indM]) {
25672             cimg::swap((*this)[indM],(*this)[mid]);
25673             if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
25674           }
25675           if ((*this)[indm]<(*this)[mid]) {
25676             cimg::swap((*this)[indm],(*this)[mid]);
25677             if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
25678           }
25679         }
25680         if (indM - indm>=3) {
25681           const T pivot = (*this)[mid];
25682           long i = indm, j = indM;
25683           if (is_increasing) {
25684             do {
25685               while ((*this)[i]<pivot) ++i;
25686               while ((*this)[j]>pivot) --j;
25687               if (i<=j) {
25688                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
25689                 cimg::swap((*this)[i++],(*this)[j--]);
25690               }
25691             } while (i<=j);
25692           } else {
25693             do {
25694               while ((*this)[i]>pivot) ++i;
25695               while ((*this)[j]<pivot) --j;
25696               if (i<=j) {
25697                 if (is_permutations) cimg::swap(permutations[i],permutations[j]);
25698                 cimg::swap((*this)[i++],(*this)[j--]);
25699               }
25700             } while (i<=j);
25701           }
25702           if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
25703           if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
25704         }
25705       }
25706       return *this;
25707     }
25708 
25709     //! Compute the SVD of the instance image, viewed as a general matrix.
25710     /**
25711        Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices
25712        and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V.
25713        \param[out] U First matrix of the SVD product.
25714        \param[out] S Coefficients of the second (diagonal) matrix of the SVD product.
25715          These coefficients are stored as a vector.
25716        \param[out] V Third matrix of the SVD product.
25717        \param sorting Tells if the diagonal coefficients are sorted (in decreasing order).
25718        \param max_iteration Maximum number of iterations considered for the algorithm convergence.
25719        \param lambda Epsilon used for the algorithm convergence.
25720        \note The instance matrix can be computed from \c U,\c S and \c V by
25721        \code
25722        const CImg<> A;  // Input matrix (assumed to contain some values).
25723        CImg<> U,S,V;
25724        A.SVD(U,S,V)
25725        \endcode
25726     **/
25727     template<typename t>
25728     const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
25729                        const unsigned int max_iteration=40, const float lambda=0) const {
25730       if (is_empty()) { U.assign(); S.assign(); V.assign(); }
25731       else {
25732         U = *this;
25733 	if (lambda!=0) {
25734 	  const unsigned int delta = std::min(U._width,U._height);
25735 	  for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
25736 	}
25737         if (S.size()<_width) S.assign(1,_width);
25738         if (V._width<_width || V._height<_height) V.assign(_width,_width);
25739         CImg<t> rv1(_width);
25740         t anorm = 0, c, f, g = 0, h, s, scale = 0;
25741         int l = 0, nm = 0;
25742 
25743         cimg_forX(U,i) {
25744           l = i + 1; rv1[i] = scale*g; g = s = scale = 0;
25745           if (i<height()) {
25746             for (int k = i; k<height(); ++k) scale+=cimg::abs(U(i,k));
25747             if (scale) {
25748               for (int k = i; k<height(); ++k) { U(i,k)/=scale; s+=U(i,k)*U(i,k); }
25749               f = U(i,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g;
25750               for (int j = l; j<width(); ++j) {
25751                 s = 0;
25752                 for (int k=i; k<height(); ++k) s+=U(i,k)*U(j,k);
25753                 f = s/h;
25754                 for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
25755               }
25756               for (int k = i; k<height(); ++k) U(i,k)*=scale;
25757             }
25758           }
25759           S[i]=scale*g;
25760 
25761           g = s = scale = 0;
25762           if (i<height() && i!=width() - 1) {
25763             for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
25764             if (scale) {
25765               for (int k = l; k<width(); ++k) { U(k,i)/= scale; s+=U(k,i)*U(k,i); }
25766               f = U(l,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g;
25767               for (int k = l; k<width(); ++k) rv1[k]=U(k,i)/h;
25768               for (int j = l; j<height(); ++j) {
25769                 s = 0;
25770                 for (int k = l; k<width(); ++k) s+=U(k,j)*U(k,i);
25771                 for (int k = l; k<width(); ++k) U(k,j)+=s*rv1[k];
25772               }
25773               for (int k = l; k<width(); ++k) U(k,i)*=scale;
25774             }
25775           }
25776           anorm = (t)std::max((float)anorm,(float)(cimg::abs(S[i]) + cimg::abs(rv1[i])));
25777         }
25778 
25779         for (int i = width() - 1; i>=0; --i) {
25780           if (i<width()-1) {
25781             if (g) {
25782               for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
25783               for (int j = l; j<width(); ++j) {
25784                 s = 0;
25785                 for (int k = l; k<width(); ++k) s+=U(k,i)*V(j,k);
25786                 for (int k = l; k<width(); ++k) V(j,k)+=s*V(i,k);
25787               }
25788             }
25789             for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.0;
25790           }
25791           V(i,i) = (t)1.0; g = rv1[i]; l = i;
25792         }
25793 
25794         for (int i = std::min(width(),height()) - 1; i>=0; --i) {
25795           l = i + 1; g = S[i];
25796           for (int j = l; j<width(); ++j) U(j,i) = 0;
25797           if (g) {
25798             g = 1/g;
25799             for (int j = l; j<width(); ++j) {
25800               s = 0; for (int k = l; k<height(); ++k) s+=U(i,k)*U(j,k);
25801               f = (s/U(i,i))*g;
25802               for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
25803             }
25804             for (int j = i; j<height(); ++j) U(i,j)*= g;
25805           } else for (int j = i; j<height(); ++j) U(i,j) = 0;
25806           ++U(i,i);
25807         }
25808 
25809         for (int k = width() - 1; k>=0; --k) {
25810           for (unsigned int its = 0; its<max_iteration; ++its) {
25811             bool flag = true;
25812             for (l = k; l>=1; --l) {
25813               nm = l - 1;
25814               if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; }
25815               if ((cimg::abs(S[nm]) + anorm)==anorm) break;
25816             }
25817             if (flag) {
25818               c = 0; s = 1;
25819               for (int i = l; i<=k; ++i) {
25820                 f = s*rv1[i]; rv1[i] = c*rv1[i];
25821                 if ((cimg::abs(f) + anorm)==anorm) break;
25822                 g = S[i]; h = cimg::_hypot(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h;
25823                 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; }
25824               }
25825             }
25826 
25827             const t z = S[k];
25828             if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
25829             nm = k - 1;
25830             t x = S[l], y = S[nm];
25831             g = rv1[nm]; h = rv1[k];
25832             f = ((y - z)*(y + z)+(g - h)*(g + h))/std::max((t)1e-25,2*h*y);
25833             g = cimg::_hypot(f,(t)1);
25834             f = ((x - z)*(x + z)+h*((y/(f + (f>=0?g:-g))) - h))/std::max((t)1e-25,x);
25835             c = s = 1;
25836             for (int j = l; j<=nm; ++j) {
25837               const int i = j + 1;
25838               g = rv1[i]; h = s*g; g = c*g;
25839               t y = S[i];
25840               t z = cimg::_hypot(f,h);
25841               rv1[j] = z; c = f/std::max((t)1e-25,z); s = h/std::max((t)1e-25,z);
25842               f = x*c + g*s; g = g*c - x*s; h = y*s; y*=c;
25843               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; }
25844               z = cimg::_hypot(f,h); S[j] = z;
25845               if (z) { z = 1/std::max((t)1e-25,z); c = f*z; s = h*z; }
25846               f = c*g + s*y; x = c*y - s*g;
25847               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; }
25848             }
25849             rv1[l] = 0; rv1[k]=f; S[k]=x;
25850           }
25851         }
25852 
25853         if (sorting) {
25854           CImg<intT> permutations;
25855           CImg<t> tmp(_width);
25856           S.sort(permutations,false);
25857           cimg_forY(U,k) {
25858             cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
25859             std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
25860           }
25861           cimg_forY(V,k) {
25862             cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
25863             std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
25864           }
25865         }
25866       }
25867       return *this;
25868     }
25869 
25870     //! Compute the SVD of the instance image, viewed as a general matrix.
25871     /**
25872        \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in
25873          SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const.
25874     **/
25875     CImgList<Tfloat> get_SVD(const bool sorting=true,
25876                              const unsigned int max_iteration=40, const float lambda=0) const {
25877       CImgList<Tfloat> res(3);
25878       SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
25879       return res;
25880     }
25881 
25882     // [internal] Compute the LU decomposition of a permuted matrix.
25883     template<typename t>
25884     CImg<T>& _LU(CImg<t>& indx, bool& d) {
25885       const int N = width();
25886       int imax = 0;
25887       CImg<Tfloat> vv(N);
25888       indx.assign(N);
25889       d = true;
25890       cimg_forX(*this,i) {
25891         Tfloat vmax = 0;
25892         cimg_forX(*this,j) {
25893           const Tfloat tmp = cimg::abs((*this)(j,i));
25894           if (tmp>vmax) vmax = tmp;
25895         }
25896         if (vmax==0) { indx.fill(0); return fill(0); }
25897         vv[i] = 1/vmax;
25898       }
25899       cimg_forX(*this,j) {
25900         for (int i = 0; i<j; ++i) {
25901           Tfloat sum=(*this)(j,i);
25902           for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
25903           (*this)(j,i) = (T)sum;
25904         }
25905         Tfloat vmax = 0;
25906         for (int i = j; i<width(); ++i) {
25907           Tfloat sum=(*this)(j,i);
25908           for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
25909           (*this)(j,i) = (T)sum;
25910           const Tfloat tmp = vv[i]*cimg::abs(sum);
25911           if (tmp>=vmax) { vmax=tmp; imax=i; }
25912         }
25913         if (j!=imax) {
25914           cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
25915           d =!d;
25916           vv[imax] = vv[j];
25917         }
25918         indx[j] = (t)imax;
25919         if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
25920         if (j<N) {
25921           const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
25922           for (int i = j + 1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
25923         }
25924       }
25925       return *this;
25926     }
25927 
25928     //! Compute minimal path in a graph, using the Dijkstra algorithm.
25929     /**
25930        \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance
25931          between two nodes (i,j).
25932        \param nb_nodes Number of graph nodes.
25933        \param starting_node Indice of the starting node.
25934        \param ending_node Indice of the ending node (set to ~0U to ignore ending node).
25935        \param previous_node Array that gives the previous node indice in the path to the starting node
25936          (optional parameter).
25937        \return Array of distances of each node to the starting node.
25938     **/
25939     template<typename tf, typename t>
25940     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
25941                             const unsigned int starting_node, const unsigned int ending_node,
25942                             CImg<t>& previous_node) {
25943       if (starting_node>=nb_nodes)
25944         throw CImgArgumentException("CImg<%s>::dijkstra(): Specified indice of starting node %u is higher "
25945                                     "than number of nodes %u.",
25946                                     pixel_type(),starting_node,nb_nodes);
25947       CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
25948       dist(starting_node) = 0;
25949       previous_node.assign(1,nb_nodes,1,1,(t)-1);
25950       previous_node(starting_node) = (t)starting_node;
25951       CImg<uintT> Q(nb_nodes);
25952       cimg_forX(Q,u) Q(u) = (unsigned int)u;
25953       cimg::swap(Q(starting_node),Q(0));
25954       unsigned int sizeQ = nb_nodes;
25955       while (sizeQ) {
25956         // Update neighbors from minimal vertex
25957         const unsigned int umin = Q(0);
25958         if (umin==ending_node) sizeQ = 0;
25959         else {
25960           const T dmin = dist(umin);
25961           const T infty = cimg::type<T>::max();
25962           for (unsigned int q = 1; q<sizeQ; ++q) {
25963             const unsigned int v = Q(q);
25964             const T d = (T)distance(v,umin);
25965             if (d<infty) {
25966               const T alt = dmin + d;
25967               if (alt<dist(v)) {
25968                 dist(v) = alt;
25969                 previous_node(v) = (t)umin;
25970                 const T distpos = dist(Q(q));
25971                 for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos + 1)/2 - 1)); pos=par)
25972                   cimg::swap(Q(pos),Q(par));
25973               }
25974             }
25975           }
25976           // Remove minimal vertex from queue
25977           Q(0) = Q(--sizeQ);
25978           const T distpos = dist(Q(0));
25979           for (unsigned int pos = 0, left = 0, right = 0;
25980                ((right=2*(pos + 1),(left=right - 1))<sizeQ && distpos>dist(Q(left))) ||
25981                  (right<sizeQ && distpos>dist(Q(right)));) {
25982             if (right<sizeQ) {
25983               if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
25984               else { cimg::swap(Q(pos),Q(right)); pos = right; }
25985             } else { cimg::swap(Q(pos),Q(left)); pos = left; }
25986           }
25987         }
25988       }
25989       return dist;
25990     }
25991 
25992     //! Return minimal path in a graph, using the Dijkstra algorithm.
25993     template<typename tf, typename t>
25994     static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
25995                             const unsigned int starting_node, const unsigned int ending_node=~0U) {
25996       CImg<uintT> foo;
25997       return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
25998     }
25999 
26000     //! Return minimal path in a graph, using the Dijkstra algorithm.
26001     /**
26002        \param starting_node Indice of the starting node.
26003        \param ending_node Indice of the ending node.
26004        \param previous_node Array that gives the previous node indice in the path to the starting node
26005          (optional parameter).
26006        \return Array of distances of each node to the starting node.
26007        \note image instance corresponds to the adjacency matrix of the graph.
26008     **/
26009     template<typename t>
26010     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node,
26011                       CImg<t>& previous_node) {
26012       return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
26013     }
26014 
26015     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
26016     template<typename t>
26017     CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node,
26018                          CImg<t>& previous_node) const {
26019       if (_width!=_height || _depth!=1 || _spectrum!=1)
26020         throw CImgInstanceException(_cimg_instance
26021                                     "dijkstra(): Instance is not a graph adjacency matrix.",
26022                                     cimg_instance);
26023 
26024       return dijkstra(*this,_width,starting_node,ending_node,previous_node);
26025     }
26026 
26027     //! Return minimal path in a graph, using the Dijkstra algorithm.
26028     CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
26029       return get_dijkstra(starting_node,ending_node).move_to(*this);
26030     }
26031 
26032     //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
26033     CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
26034       CImg<uintT> foo;
26035       return get_dijkstra(starting_node,ending_node,foo);
26036     }
26037 
26038     //! Return an image containing the ascii codes of the specified  string.
26039     /**
26040        \param str input C-string to encode as an image.
26041        \param is_last_zero Tells if the ending \c '0' character appear in the resulting image.
26042        \param is_shared Return result that shares its buffer with \p str.
26043     **/
26044     static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) {
26045       if (!str) return CImg<T>();
26046       return CImg<T>(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared);
26047     }
26048 
26049     //! Return a \c 1x1 image containing specified value.
26050     /**
26051        \param a0 First vector value.
26052     **/
26053     static CImg<T> vector(const T& a0) {
26054       CImg<T> r(1,1);
26055       r[0] = a0;
26056       return r;
26057     }
26058 
26059     //! Return a \c 1x2 image containing specified values.
26060     /**
26061        \param a0 First vector value.
26062        \param a1 Second vector value.
26063     **/
26064     static CImg<T> vector(const T& a0, const T& a1) {
26065       CImg<T> r(1,2); T *ptr = r._data;
26066       *(ptr++) = a0; *(ptr++) = a1;
26067       return r;
26068     }
26069 
26070     //! Return a \c 1x3 image containing specified values.
26071     /**
26072        \param a0 First vector value.
26073        \param a1 Second vector value.
26074        \param a2 Third vector value.
26075     **/
26076     static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
26077       CImg<T> r(1,3); T *ptr = r._data;
26078       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
26079       return r;
26080     }
26081 
26082     //! Return a \c 1x4 image containing specified values.
26083     /**
26084        \param a0 First vector value.
26085        \param a1 Second vector value.
26086        \param a2 Third vector value.
26087        \param a3 Fourth vector value.
26088     **/
26089     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
26090       CImg<T> r(1,4); T *ptr = r._data;
26091       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26092       return r;
26093     }
26094 
26095     //! Return a \c 1x5 image containing specified values.
26096     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
26097       CImg<T> r(1,5); T *ptr = r._data;
26098       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
26099       return r;
26100     }
26101 
26102     //! Return a \c 1x6 image containing specified values.
26103     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
26104       CImg<T> r(1,6); T *ptr = r._data;
26105       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
26106       return r;
26107     }
26108 
26109     //! Return a \c 1x7 image containing specified values.
26110     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26111 			  const T& a4, const T& a5, const T& a6) {
26112       CImg<T> r(1,7); T *ptr = r._data;
26113       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26114       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6;
26115       return r;
26116     }
26117 
26118     //! Return a \c 1x8 image containing specified values.
26119     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26120 			  const T& a4, const T& a5, const T& a6, const T& a7) {
26121       CImg<T> r(1,8); T *ptr = r._data;
26122       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26123       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26124       return r;
26125     }
26126 
26127     //! Return a \c 1x9 image containing specified values.
26128     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26129 			  const T& a4, const T& a5, const T& a6, const T& a7,
26130 			  const T& a8) {
26131       CImg<T> r(1,9); T *ptr = r._data;
26132       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26133       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26134       *(ptr++) = a8;
26135       return r;
26136     }
26137 
26138     //! Return a \c 1x10 image containing specified values.
26139     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26140                           const T& a4, const T& a5, const T& a6, const T& a7,
26141                           const T& a8, const T& a9) {
26142       CImg<T> r(1,10); T *ptr = r._data;
26143       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26144       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26145       *(ptr++) = a8; *(ptr++) = a9;
26146       return r;
26147     }
26148 
26149     //! Return a \c 1x11 image containing specified values.
26150     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26151                           const T& a4, const T& a5, const T& a6, const T& a7,
26152                           const T& a8, const T& a9, const T& a10) {
26153       CImg<T> r(1,11); T *ptr = r._data;
26154       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26155       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26156       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10;
26157       return r;
26158     }
26159 
26160     //! Return a \c 1x12 image containing specified values.
26161     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26162                           const T& a4, const T& a5, const T& a6, const T& a7,
26163                           const T& a8, const T& a9, const T& a10, const T& a11) {
26164       CImg<T> r(1,12); T *ptr = r._data;
26165       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26166       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26167       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
26168       return r;
26169     }
26170 
26171     //! Return a \c 1x13 image containing specified values.
26172     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26173                           const T& a4, const T& a5, const T& a6, const T& a7,
26174                           const T& a8, const T& a9, const T& a10, const T& a11,
26175 			  const T& a12) {
26176       CImg<T> r(1,13); T *ptr = r._data;
26177       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26178       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26179       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
26180       *(ptr++) = a12;
26181       return r;
26182     }
26183 
26184     //! Return a \c 1x14 image containing specified values.
26185     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26186                           const T& a4, const T& a5, const T& a6, const T& a7,
26187                           const T& a8, const T& a9, const T& a10, const T& a11,
26188 			  const T& a12, const T& a13) {
26189       CImg<T> r(1,14); T *ptr = r._data;
26190       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26191       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26192       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
26193       *(ptr++) = a12; *(ptr++) = a13;
26194       return r;
26195     }
26196 
26197     //! Return a \c 1x15 image containing specified values.
26198     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26199                           const T& a4, const T& a5, const T& a6, const T& a7,
26200                           const T& a8, const T& a9, const T& a10, const T& a11,
26201 			  const T& a12, const T& a13, const T& a14) {
26202       CImg<T> r(1,15); T *ptr = r._data;
26203       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26204       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26205       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
26206       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
26207       return r;
26208     }
26209 
26210     //! Return a \c 1x16 image containing specified values.
26211     static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
26212                           const T& a4, const T& a5, const T& a6, const T& a7,
26213                           const T& a8, const T& a9, const T& a10, const T& a11,
26214 			  const T& a12, const T& a13, const T& a14, const T& a15) {
26215       CImg<T> r(1,16); T *ptr = r._data;
26216       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26217       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26218       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
26219       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
26220       return r;
26221     }
26222 
26223     //! Return a 1x1 matrix containing specified coefficients.
26224     /**
26225        \param a0 First matrix value.
26226        \note Equivalent to vector(const T&).
26227     **/
26228     static CImg<T> matrix(const T& a0) {
26229       return vector(a0);
26230     }
26231 
26232     //! Return a 2x2 matrix containing specified coefficients.
26233     /**
26234        \param a0 First matrix value.
26235        \param a1 Second matrix value.
26236        \param a2 Third matrix value.
26237        \param a3 Fourth matrix value.
26238     **/
26239     static CImg<T> matrix(const T& a0, const T& a1,
26240 			  const T& a2, const T& a3) {
26241       CImg<T> r(2,2); T *ptr = r._data;
26242       *(ptr++) = a0; *(ptr++) = a1;
26243       *(ptr++) = a2; *(ptr++) = a3;
26244       return r;
26245     }
26246 
26247     //! Return a 3x3 matrix containing specified coefficients.
26248     /**
26249        \param a0 First matrix value.
26250        \param a1 Second matrix value.
26251        \param a2 Third matrix value.
26252        \param a3 Fourth matrix value.
26253        \param a4 Fifth matrix value.
26254        \param a5 Sixth matrix value.
26255        \param a6 Seventh matrix value.
26256        \param a7 Eighth matrix value.
26257        \param a8 Nineth matrix value.
26258     **/
26259     static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
26260 			  const T& a3, const T& a4, const T& a5,
26261 			  const T& a6, const T& a7, const T& a8) {
26262       CImg<T> r(3,3); T *ptr = r._data;
26263       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
26264       *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
26265       *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
26266       return r;
26267     }
26268 
26269     //! Return a 4x4 matrix containing specified coefficients.
26270     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
26271 			  const T& a4, const T& a5, const T& a6, const T& a7,
26272 			  const T& a8, const T& a9, const T& a10, const T& a11,
26273 			  const T& a12, const T& a13, const T& a14, const T& a15) {
26274       CImg<T> r(4,4); T *ptr = r._data;
26275       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
26276       *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
26277       *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
26278       *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
26279       return r;
26280     }
26281 
26282     //! Return a 5x5 matrix containing specified coefficients.
26283     static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
26284                           const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
26285                           const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
26286                           const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
26287                           const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
26288       CImg<T> r(5,5); T *ptr = r._data;
26289       *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
26290       *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
26291       *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
26292       *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
26293       *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
26294       return r;
26295     }
26296 
26297     //! Return a 1x1 symmetric matrix containing specified coefficients.
26298     /**
26299        \param a0 First matrix value.
26300        \note Equivalent to vector(const T&).
26301     **/
26302     static CImg<T> tensor(const T& a0) {
26303       return matrix(a0);
26304     }
26305 
26306     //! Return a 2x2 symmetric matrix tensor containing specified coefficients.
26307     static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
26308       return matrix(a0,a1,a1,a2);
26309     }
26310 
26311     //! Return a 3x3 symmetric matrix containing specified coefficients.
26312     static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
26313       return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
26314     }
26315 
26316     //! Return a 1x1 diagonal matrix containing specified coefficients.
26317     static CImg<T> diagonal(const T& a0) {
26318       return matrix(a0);
26319     }
26320 
26321     //! Return a 2x2 diagonal matrix containing specified coefficients.
26322     static CImg<T> diagonal(const T& a0, const T& a1) {
26323       return matrix(a0,0,0,a1);
26324     }
26325 
26326     //! Return a 3x3 diagonal matrix containing specified coefficients.
26327     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
26328       return matrix(a0,0,0,0,a1,0,0,0,a2);
26329     }
26330 
26331     //! Return a 4x4 diagonal matrix containing specified coefficients.
26332     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
26333       return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
26334     }
26335 
26336     //! Return a 5x5 diagonal matrix containing specified coefficients.
26337     static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
26338       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);
26339     }
26340 
26341     //! Return a NxN identity matrix.
26342     /**
26343        \param N Dimension of the matrix.
26344     **/
26345     static CImg<T> identity_matrix(const unsigned int N) {
26346       CImg<T> res(N,N,1,1,0);
26347       cimg_forX(res,x) res(x,x) = 1;
26348       return res;
26349     }
26350 
26351     //! Return a N-numbered sequence vector from \p a0 to \p a1.
26352     /**
26353        \param N Size of the resulting vector.
26354        \param a0 Starting value of the sequence.
26355        \param a1 Ending value of the sequence.
26356      **/
26357     static CImg<T> sequence(const unsigned int N, const T& a0, const T& a1) {
26358       if (N) return CImg<T>(1,N).sequence(a0,a1);
26359       return CImg<T>();
26360     }
26361 
26362     //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion.
26363     /**
26364        \param x X-coordinate of the rotation axis, or first quaternion coordinate.
26365        \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
26366        \param z Z-coordinate of the rotation axis, or third quaternion coordinate.
26367        \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
26368        \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
26369      **/
26370     static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w,
26371                                    const bool is_quaternion=false) {
26372       double X, Y, Z, W, N;
26373       if (is_quaternion) {
26374         N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w);
26375         if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; }
26376         else { X = Y = Z = 0; W = 1; }
26377         return CImg<T>::matrix((T)(X*X + Y*Y - Z*Z - W*W),(T)(2*Y*Z - 2*X*W),(T)(2*X*Z + 2*Y*W),
26378                                (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y),
26379                                (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W));
26380       }
26381       N = cimg::hypot((double)x,(double)y,(double)z);
26382       if (N>0) { X = x/N; Y = y/N; Z = z/N; }
26383       else { X = Y = 0; Z = 1; }
26384       const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang);
26385       return CImg<T>::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s),
26386                              (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s),
26387                              (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c));
26388     }
26389 
26390     //@}
26391     //-----------------------------------
26392     //
26393     //! \name Value Manipulation
26394     //@{
26395     //-----------------------------------
26396 
26397     //! Fill all pixel values with specified value.
26398     /**
26399        \param val Fill value.
26400     **/
26401     CImg<T>& fill(const T& val) {
26402       if (is_empty()) return *this;
26403       if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
26404       else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*)
26405       return *this;
26406     }
26407 
26408     //! Fill all pixel values with specified value \newinstance.
26409     CImg<T> get_fill(const T& val) const {
26410       return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
26411     }
26412 
26413     //! Fill sequentially all pixel values with specified values.
26414     /**
26415        \param val0 First fill value.
26416        \param val1 Second fill value.
26417     **/
26418     CImg<T>& fill(const T& val0, const T& val1) {
26419       if (is_empty()) return *this;
26420       T *ptrd, *ptre = end() - 1;
26421       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
26422       if (ptrd!=ptre + 1) *(ptrd++) = val0;
26423       return *this;
26424     }
26425 
26426     //! Fill sequentially all pixel values with specified values \newinstance.
26427     CImg<T> get_fill(const T& val0, const T& val1) const {
26428       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
26429     }
26430 
26431     //! Fill sequentially all pixel values with specified values \overloading.
26432     CImg<T>& fill(const T& val0, const T& val1, const T& val2) {
26433       if (is_empty()) return *this;
26434       T *ptrd, *ptre = end() - 2;
26435       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
26436       ptre+=2;
26437       switch (ptre - ptrd) {
26438       case 2 : *(--ptre) = val1; // fallthrough
26439       case 1 : *(--ptre) = val0; // fallthrough
26440       }
26441       return *this;
26442     }
26443 
26444     //! Fill sequentially all pixel values with specified values \newinstance.
26445     CImg<T> get_fill(const T& val0, const T& val1, const T& val2) const {
26446       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
26447     }
26448 
26449     //! Fill sequentially all pixel values with specified values \overloading.
26450     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3) {
26451       if (is_empty()) return *this;
26452       T *ptrd, *ptre = end() - 3;
26453       for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
26454       ptre+=3;
26455       switch (ptre - ptrd) {
26456       case 3 : *(--ptre) = val2; // fallthrough
26457       case 2 : *(--ptre) = val1; // fallthrough
26458       case 1 : *(--ptre) = val0; // fallthrough
26459       }
26460       return *this;
26461     }
26462 
26463     //! Fill sequentially all pixel values with specified values \newinstance.
26464     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const {
26465       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
26466     }
26467 
26468     //! Fill sequentially all pixel values with specified values \overloading.
26469     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) {
26470       if (is_empty()) return *this;
26471       T *ptrd, *ptre = end() - 4;
26472       for (ptrd = _data; ptrd<ptre; ) {
26473         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
26474       }
26475       ptre+=4;
26476       switch (ptre - ptrd) {
26477       case 4 : *(--ptre) = val3; // fallthrough
26478       case 3 : *(--ptre) = val2; // fallthrough
26479       case 2 : *(--ptre) = val1; // fallthrough
26480       case 1 : *(--ptre) = val0; // fallthrough
26481       }
26482       return *this;
26483     }
26484 
26485     //! Fill sequentially all pixel values with specified values \newinstance.
26486     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const {
26487       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
26488     }
26489 
26490     //! Fill sequentially all pixel values with specified values \overloading.
26491     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) {
26492       if (is_empty()) return *this;
26493       T *ptrd, *ptre = end() - 5;
26494       for (ptrd = _data; ptrd<ptre; ) {
26495         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26496       }
26497       ptre+=5;
26498       switch (ptre - ptrd) {
26499       case 5 : *(--ptre) = val4; // fallthrough
26500       case 4 : *(--ptre) = val3; // fallthrough
26501       case 3 : *(--ptre) = val2; // fallthrough
26502       case 2 : *(--ptre) = val1; // fallthrough
26503       case 1 : *(--ptre) = val0; // fallthrough
26504       }
26505       return *this;
26506     }
26507 
26508     //! Fill sequentially all pixel values with specified values \newinstance.
26509     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const {
26510       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
26511     }
26512 
26513     //! Fill sequentially all pixel values with specified values \overloading.
26514     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26515                   const T& val6) {
26516       if (is_empty()) return *this;
26517       T *ptrd, *ptre = end() - 6;
26518       for (ptrd = _data; ptrd<ptre; ) {
26519         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26520         *(ptrd++) = val6;
26521       }
26522       ptre+=6;
26523       switch (ptre - ptrd) {
26524       case 6 : *(--ptre) = val5; // fallthrough
26525       case 5 : *(--ptre) = val4; // fallthrough
26526       case 4 : *(--ptre) = val3; // fallthrough
26527       case 3 : *(--ptre) = val2; // fallthrough
26528       case 2 : *(--ptre) = val1; // fallthrough
26529       case 1 : *(--ptre) = val0; // fallthrough
26530       }
26531       return *this;
26532     }
26533 
26534     //! Fill sequentially all pixel values with specified values \newinstance.
26535     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26536                      const T& val6) const {
26537       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
26538     }
26539 
26540     //! Fill sequentially all pixel values with specified values \overloading.
26541     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26542                   const T& val6, const T& val7) {
26543       if (is_empty()) return *this;
26544       T *ptrd, *ptre = end() - 7;
26545       for (ptrd = _data; ptrd<ptre; ) {
26546         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
26547         *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
26548       }
26549       ptre+=7;
26550       switch (ptre - ptrd) {
26551       case 7 : *(--ptre) = val6; // fallthrough
26552       case 6 : *(--ptre) = val5; // fallthrough
26553       case 5 : *(--ptre) = val4; // fallthrough
26554       case 4 : *(--ptre) = val3; // fallthrough
26555       case 3 : *(--ptre) = val2; // fallthrough
26556       case 2 : *(--ptre) = val1; // fallthrough
26557       case 1 : *(--ptre) = val0; // fallthrough
26558       }
26559       return *this;
26560     }
26561 
26562     //! Fill sequentially all pixel values with specified values \newinstance.
26563     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26564                      const T& val6, const T& val7) const {
26565       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
26566     }
26567 
26568     //! Fill sequentially all pixel values with specified values \overloading.
26569     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26570                   const T& val6, const T& val7, const T& val8) {
26571       if (is_empty()) return *this;
26572       T *ptrd, *ptre = end() - 8;
26573       for (ptrd = _data; ptrd<ptre; ) {
26574         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
26575         *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26576         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
26577       }
26578       ptre+=8;
26579       switch (ptre - ptrd) {
26580       case 8 : *(--ptre) = val7; // fallthrough
26581       case 7 : *(--ptre) = val6; // fallthrough
26582       case 6 : *(--ptre) = val5; // fallthrough
26583       case 5 : *(--ptre) = val4; // fallthrough
26584       case 4 : *(--ptre) = val3; // fallthrough
26585       case 3 : *(--ptre) = val2; // fallthrough
26586       case 2 : *(--ptre) = val1; // fallthrough
26587       case 1 : *(--ptre) = val0; // fallthrough
26588       }
26589       return *this;
26590     }
26591 
26592     //! Fill sequentially all pixel values with specified values \newinstance.
26593     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26594                      const T& val6, const T& val7, const T& val8) const {
26595       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
26596     }
26597 
26598     //! Fill sequentially all pixel values with specified values \overloading.
26599     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26600                   const T& val6, const T& val7, const T& val8, const T& val9) {
26601       if (is_empty()) return *this;
26602       T *ptrd, *ptre = end() - 9;
26603       for (ptrd = _data; ptrd<ptre; ) {
26604         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
26605         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
26606       }
26607       ptre+=9;
26608       switch (ptre - ptrd) {
26609       case 9 : *(--ptre) = val8; // fallthrough
26610       case 8 : *(--ptre) = val7; // fallthrough
26611       case 7 : *(--ptre) = val6; // fallthrough
26612       case 6 : *(--ptre) = val5; // fallthrough
26613       case 5 : *(--ptre) = val4; // fallthrough
26614       case 4 : *(--ptre) = val3; // fallthrough
26615       case 3 : *(--ptre) = val2; // fallthrough
26616       case 2 : *(--ptre) = val1; // fallthrough
26617       case 1 : *(--ptre) = val0; // fallthrough
26618       }
26619       return *this;
26620     }
26621 
26622     //! Fill sequentially all pixel values with specified values \newinstance.
26623     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26624                      const T& val6, const T& val7, const T& val8, const T& val9) const {
26625       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
26626     }
26627 
26628     //! Fill sequentially all pixel values with specified values \overloading.
26629     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26630                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) {
26631       if (is_empty()) return *this;
26632       T *ptrd, *ptre = end() - 10;
26633       for (ptrd = _data; ptrd<ptre; ) {
26634         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
26635         *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
26636         *(ptrd++) = val10;
26637       }
26638       ptre+=10;
26639       switch (ptre - ptrd) {
26640       case 10 : *(--ptre) = val9; // fallthrough
26641       case 9 : *(--ptre) = val8; // fallthrough
26642       case 8 : *(--ptre) = val7; // fallthrough
26643       case 7 : *(--ptre) = val6; // fallthrough
26644       case 6 : *(--ptre) = val5; // fallthrough
26645       case 5 : *(--ptre) = val4; // fallthrough
26646       case 4 : *(--ptre) = val3; // fallthrough
26647       case 3 : *(--ptre) = val2; // fallthrough
26648       case 2 : *(--ptre) = val1; // fallthrough
26649       case 1 : *(--ptre) = val0; // fallthrough
26650       }
26651       return *this;
26652     }
26653 
26654     //! Fill sequentially all pixel values with specified values \newinstance.
26655     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26656                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const {
26657       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
26658     }
26659 
26660     //! Fill sequentially all pixel values with specified values \overloading.
26661     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26662                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) {
26663       if (is_empty()) return *this;
26664       T *ptrd, *ptre = end() - 11;
26665       for (ptrd = _data; ptrd<ptre; ) {
26666         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26667         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26668       }
26669       ptre+=11;
26670       switch (ptre - ptrd) {
26671       case 11 : *(--ptre) = val10; // fallthrough
26672       case 10 : *(--ptre) = val9; // fallthrough
26673       case 9 : *(--ptre) = val8; // fallthrough
26674       case 8 : *(--ptre) = val7; // fallthrough
26675       case 7 : *(--ptre) = val6; // fallthrough
26676       case 6 : *(--ptre) = val5; // fallthrough
26677       case 5 : *(--ptre) = val4; // fallthrough
26678       case 4 : *(--ptre) = val3; // fallthrough
26679       case 3 : *(--ptre) = val2; // fallthrough
26680       case 2 : *(--ptre) = val1; // fallthrough
26681       case 1 : *(--ptre) = val0; // fallthrough
26682       }
26683       return *this;
26684     }
26685 
26686     //! Fill sequentially all pixel values with specified values \newinstance.
26687     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26688                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const {
26689       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26690                                                            val11);
26691     }
26692 
26693     //! Fill sequentially all pixel values with specified values \overloading.
26694     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26695                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26696                   const T& val12) {
26697       if (is_empty()) return *this;
26698       T *ptrd, *ptre = end() - 12;
26699       for (ptrd = _data; ptrd<ptre; ) {
26700         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26701         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26702         *(ptrd++) = val12;
26703       }
26704       ptre+=12;
26705       switch (ptre - ptrd) {
26706       case 12 : *(--ptre) = val11; // fallthrough
26707       case 11 : *(--ptre) = val10; // fallthrough
26708       case 10 : *(--ptre) = val9; // fallthrough
26709       case 9 : *(--ptre) = val8; // fallthrough
26710       case 8 : *(--ptre) = val7; // fallthrough
26711       case 7 : *(--ptre) = val6; // fallthrough
26712       case 6 : *(--ptre) = val5; // fallthrough
26713       case 5 : *(--ptre) = val4; // fallthrough
26714       case 4 : *(--ptre) = val3; // fallthrough
26715       case 3 : *(--ptre) = val2; // fallthrough
26716       case 2 : *(--ptre) = val1; // fallthrough
26717       case 1 : *(--ptre) = val0; // fallthrough
26718       }
26719       return *this;
26720     }
26721 
26722     //! Fill sequentially all pixel values with specified values \newinstance.
26723     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26724                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26725                      const T& val12) const {
26726       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26727                                                            val11,val12);
26728     }
26729 
26730     //! Fill sequentially all pixel values with specified values \overloading.
26731     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26732                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26733                   const T& val12, const T& val13) {
26734       if (is_empty()) return *this;
26735       T *ptrd, *ptre = end() - 13;
26736       for (ptrd = _data; ptrd<ptre; ) {
26737         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26738         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26739         *(ptrd++) = val12; *(ptrd++) = val13;
26740       }
26741       ptre+=13;
26742       switch (ptre - ptrd) {
26743       case 13 : *(--ptre) = val12; // fallthrough
26744       case 12 : *(--ptre) = val11; // fallthrough
26745       case 11 : *(--ptre) = val10; // fallthrough
26746       case 10 : *(--ptre) = val9; // fallthrough
26747       case 9 : *(--ptre) = val8; // fallthrough
26748       case 8 : *(--ptre) = val7; // fallthrough
26749       case 7 : *(--ptre) = val6; // fallthrough
26750       case 6 : *(--ptre) = val5; // fallthrough
26751       case 5 : *(--ptre) = val4; // fallthrough
26752       case 4 : *(--ptre) = val3; // fallthrough
26753       case 3 : *(--ptre) = val2; // fallthrough
26754       case 2 : *(--ptre) = val1; // fallthrough
26755       case 1 : *(--ptre) = val0; // fallthrough
26756       }
26757       return *this;
26758     }
26759 
26760     //! Fill sequentially all pixel values with specified values \newinstance.
26761     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26762                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26763                      const T& val12, const T& val13) const {
26764       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26765                                                            val11,val12,val13);
26766     }
26767 
26768     //! Fill sequentially all pixel values with specified values \overloading.
26769     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26770                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26771                   const T& val12, const T& val13, const T& val14) {
26772       if (is_empty()) return *this;
26773       T *ptrd, *ptre = end() - 14;
26774       for (ptrd = _data; ptrd<ptre; ) {
26775         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26776         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26777         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
26778       }
26779       ptre+=14;
26780       switch (ptre - ptrd) {
26781       case 14 : *(--ptre) = val13; // fallthrough
26782       case 13 : *(--ptre) = val12; // fallthrough
26783       case 12 : *(--ptre) = val11; // fallthrough
26784       case 11 : *(--ptre) = val10; // fallthrough
26785       case 10 : *(--ptre) = val9; // fallthrough
26786       case 9 : *(--ptre) = val8; // fallthrough
26787       case 8 : *(--ptre) = val7; // fallthrough
26788       case 7 : *(--ptre) = val6; // fallthrough
26789       case 6 : *(--ptre) = val5; // fallthrough
26790       case 5 : *(--ptre) = val4; // fallthrough
26791       case 4 : *(--ptre) = val3; // fallthrough
26792       case 3 : *(--ptre) = val2; // fallthrough
26793       case 2 : *(--ptre) = val1; // fallthrough
26794       case 1 : *(--ptre) = val0; // fallthrough
26795       }
26796       return *this;
26797     }
26798 
26799     //! Fill sequentially all pixel values with specified values \newinstance.
26800     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26801                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26802                      const T& val12, const T& val13, const T& val14) const {
26803       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26804                                                            val11,val12,val13,val14);
26805     }
26806 
26807     //! Fill sequentially all pixel values with specified values \overloading.
26808     CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26809                   const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26810                   const T& val12, const T& val13, const T& val14, const T& val15) {
26811       if (is_empty()) return *this;
26812       T *ptrd, *ptre = end() - 15;
26813       for (ptrd = _data; ptrd<ptre; ) {
26814         *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
26815         *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
26816         *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
26817       }
26818       ptre+=15;
26819       switch (ptre - ptrd) {
26820       case 15 : *(--ptre) = val14; // fallthrough
26821       case 14 : *(--ptre) = val13; // fallthrough
26822       case 13 : *(--ptre) = val12; // fallthrough
26823       case 12 : *(--ptre) = val11; // fallthrough
26824       case 11 : *(--ptre) = val10; // fallthrough
26825       case 10 : *(--ptre) = val9; // fallthrough
26826       case 9 : *(--ptre) = val8; // fallthrough
26827       case 8 : *(--ptre) = val7; // fallthrough
26828       case 7 : *(--ptre) = val6; // fallthrough
26829       case 6 : *(--ptre) = val5; // fallthrough
26830       case 5 : *(--ptre) = val4; // fallthrough
26831       case 4 : *(--ptre) = val3; // fallthrough
26832       case 3 : *(--ptre) = val2; // fallthrough
26833       case 2 : *(--ptre) = val1; // fallthrough
26834       case 1 : *(--ptre) = val0; // fallthrough
26835       }
26836       return *this;
26837     }
26838 
26839     //! Fill sequentially all pixel values with specified values \newinstance.
26840     CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
26841                      const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
26842                      const T& val12, const T& val13, const T& val14, const T& val15) const {
26843       return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
26844                                                            val11,val12,val13,val14,val15);
26845     }
26846 
26847     //! Fill sequentially pixel values according to a given expression.
26848     /**
26849        \param expression C-string describing a math formula, or a sequence of values.
26850        \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling.
26851        \param allow_formula Tells that mathematical formulas are authorized for the filling.
26852        \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression.
26853        \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression.
26854     **/
26855     CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
26856                   const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) {
26857       return _fill(expression,repeat_values,allow_formula,list_inputs,list_outputs,"fill",0);
26858     }
26859 
26860     CImg<T>& _fill(const char *const expression, const bool repeat_values, bool allow_formula,
26861                    const CImgList<T> *const list_inputs, CImgList<T> *const list_outputs,
26862                    const char *const calling_function, const CImg<T> *provides_copy) {
26863       if (is_empty() || !expression || !*expression) return *this;
26864       const unsigned int omode = cimg::exception_mode();
26865       cimg::exception_mode(0);
26866       CImg<charT> is_error;
26867       bool is_value_sequence = false;
26868       cimg_abort_init;
26869 
26870       if (allow_formula) {
26871 
26872         // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser.
26873         double value;
26874         char sep;
26875         const int err = cimg_sscanf(expression,"%lf %c",&value,&sep);
26876         if (err==1 || (err==2 && sep==',')) {
26877           if (err==1) return fill((T)value);
26878           else is_value_sequence = true;
26879         }
26880 
26881         // Try to fill values according to a formula.
26882         _cimg_abort_init_omp;
26883         if (!is_value_sequence) try {
26884             CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
26885             _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' ||
26886                                                *expression=='*' || *expression==':'),
26887                                  calling_function,base,this,list_inputs,list_outputs,true);
26888             if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' &&
26889                 mp.need_input_copy)
26890               base.assign().assign(*this,false); // Needs input copy
26891 
26892             bool do_in_parallel = false;
26893 #ifdef cimg_use_openmp
26894             cimg_openmp_if(*expression=='*' || *expression==':' ||
26895                            (mp.is_parallelizable && _width>=320 && _height*_depth*_spectrum>=2))
26896               do_in_parallel = true;
26897 #endif
26898             if (mp.result_dim) { // Vector-valued expression
26899               const unsigned int N = std::min(mp.result_dim,_spectrum);
26900               const ulongT whd = (ulongT)_width*_height*_depth;
26901               T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
26902               if (*expression=='<') {
26903                 CImg<doubleT> res(1,mp.result_dim);
26904                 cimg_rofYZ(*this,y,z) {
26905                   cimg_abort_test;
26906                   cimg_rofX(*this,x) {
26907                     mp(x,y,z,0,res._data);
26908                     const double *ptrs = res._data;
26909                     T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
26910                   }
26911                 }
26912               } else if (*expression=='>' || !do_in_parallel) {
26913                 CImg<doubleT> res(1,mp.result_dim);
26914                 cimg_forYZ(*this,y,z) {
26915                   cimg_abort_test;
26916                   cimg_forX(*this,x) {
26917                     mp(x,y,z,0,res._data);
26918                     const double *ptrs = res._data;
26919                     T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
26920                   }
26921                 }
26922              } else {
26923 #ifdef cimg_use_openmp
26924                 cimg_pragma_openmp(parallel)
26925                 {
26926                   _cimg_math_parser
26927                     _mp = omp_get_thread_num()?mp:_cimg_math_parser(),
26928                     &lmp = omp_get_thread_num()?_mp:mp;
26929                   lmp.is_fill = true;
26930                   cimg_pragma_openmp(for collapse(2))
26931                     cimg_forYZ(*this,y,z) _cimg_abort_try_omp {
26932                     cimg_abort_test;
26933                     CImg<doubleT> res(1,lmp.result_dim);
26934                     T *ptrd = data(0,y,z,0);
26935                     cimg_forX(*this,x) {
26936                       lmp(x,y,z,0,res._data);
26937                       const double *ptrs = res._data;
26938                       T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
26939                     }
26940                   } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp
26941                 }
26942 #endif
26943               }
26944 
26945             } else { // Scalar-valued expression
26946               T *ptrd = *expression=='<'?end() - 1:_data;
26947               if (*expression=='<')
26948                 cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); }
26949               else if (*expression=='>' || !do_in_parallel)
26950                 cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
26951               else {
26952 #ifdef cimg_use_openmp
26953                 cimg_pragma_openmp(parallel)
26954                 {
26955                   _cimg_math_parser
26956                     _mp = omp_get_thread_num()?mp:_cimg_math_parser(),
26957                     &lmp = omp_get_thread_num()?_mp:mp;
26958                   lmp.is_fill = true;
26959                   cimg_pragma_openmp(for collapse(3))
26960                     cimg_forYZC(*this,y,z,c) _cimg_abort_try_omp {
26961                     cimg_abort_test;
26962                     T *ptrd = data(0,y,z,c);
26963                     cimg_forX(*this,x) *ptrd++ = (T)lmp(x,y,z,c);
26964                   } _cimg_abort_catch_omp _cimg_abort_catch_fill_omp
26965                 }
26966 #endif
26967               }
26968             }
26969             mp.end();
26970           } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error); }
26971       }
26972 
26973       // Try to fill values according to a value sequence.
26974       if (!allow_formula || is_value_sequence || is_error) {
26975         CImg<charT> item(256);
26976         char sep = 0;
26977         const char *nexpression = expression;
26978         ulongT nb = 0;
26979         const ulongT siz = size();
26980         T *ptrd = _data;
26981         for (double val = 0; *nexpression && nb<siz; ++nb) {
26982           sep = 0;
26983           const int err = cimg_sscanf(nexpression,"%255[ \n\t0-9.eEinfa+-]%c",item._data,&sep);
26984           if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) {
26985             nexpression+=std::strlen(item) + (err>1);
26986             *(ptrd++) = (T)val;
26987           } else break;
26988         }
26989         cimg::exception_mode(omode);
26990         if (nb<siz && (sep || *nexpression)) {
26991           if (is_error) throw CImgArgumentException("%s",is_error._data);
26992           else throw CImgArgumentException(_cimg_instance
26993                                            "%s(): Invalid sequence of filling values '%s'.",
26994                                            cimg_instance,calling_function,expression);
26995         }
26996         if (repeat_values && nb && nb<siz)
26997           for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
26998       }
26999 
27000       cimg::exception_mode(omode);
27001       cimg_abort_test;
27002       return *this;
27003     }
27004 
27005     //! Fill sequentially pixel values according to a given expression \newinstance.
27006     CImg<T> get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
27007                      const CImgList<T> *const list_inputs=0, CImgList<T> *const list_outputs=0) const {
27008       return (+*this).fill(expression,repeat_values,allow_formula,list_inputs,list_outputs);
27009     }
27010 
27011     //! Fill sequentially pixel values according to the values found in another image.
27012     /**
27013        \param values Image containing the values used for the filling.
27014        \param repeat_values In case there are less values than necessary in \c values, tells if these values must be
27015          repeated for the filling.
27016     **/
27017     template<typename t>
27018     CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
27019       if (is_empty() || !values) return *this;
27020       T *ptrd = _data, *ptre = ptrd + size();
27021       for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs)
27022         *(ptrd++) = (T)*ptrs;
27023       if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
27024       return *this;
27025     }
27026 
27027     //! Fill sequentially pixel values according to the values found in another image \newinstance.
27028     template<typename t>
27029     CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
27030       return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):
27031         (+*this).fill(values,repeat_values);
27032     }
27033 
27034     //! Fill pixel values along the X-axis at a specified pixel position.
27035     /**
27036        \param y Y-coordinate of the filled column.
27037        \param z Z-coordinate of the filled column.
27038        \param c C-coordinate of the filled column.
27039        \param a0 First fill value.
27040     **/
27041     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
27042 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
27043     va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
27044     for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
27045     va_end(ap); }
27046       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
27047       return *this;
27048     }
27049 
27050     //! Fill pixel values along the X-axis at a specified pixel position \overloading.
27051     CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
27052       if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
27053       return *this;
27054     }
27055 
27056     //! Fill pixel values along the Y-axis at a specified pixel position.
27057     /**
27058        \param x X-coordinate of the filled row.
27059        \param z Z-coordinate of the filled row.
27060        \param c C-coordinate of the filled row.
27061        \param a0 First fill value.
27062     **/
27063     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
27064       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
27065       return *this;
27066     }
27067 
27068     //! Fill pixel values along the Y-axis at a specified pixel position \overloading.
27069     CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
27070       if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
27071       return *this;
27072     }
27073 
27074     //! Fill pixel values along the Z-axis at a specified pixel position.
27075     /**
27076        \param x X-coordinate of the filled slice.
27077        \param y Y-coordinate of the filled slice.
27078        \param c C-coordinate of the filled slice.
27079        \param a0 First fill value.
27080     **/
27081     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
27082       const ulongT wh = (ulongT)_width*_height;
27083       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
27084       return *this;
27085     }
27086 
27087     //! Fill pixel values along the Z-axis at a specified pixel position \overloading.
27088     CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
27089       const ulongT wh = (ulongT)_width*_height;
27090       if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
27091       return *this;
27092     }
27093 
27094     //! Fill pixel values along the C-axis at a specified pixel position.
27095     /**
27096        \param x X-coordinate of the filled channel.
27097        \param y Y-coordinate of the filled channel.
27098        \param z Z-coordinate of the filled channel.
27099        \param a0 First filling value.
27100     **/
27101     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
27102       const ulongT whd = (ulongT)_width*_height*_depth;
27103       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
27104       return *this;
27105     }
27106 
27107     //! Fill pixel values along the C-axis at a specified pixel position \overloading.
27108     CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
27109       const ulongT whd = (ulongT)_width*_height*_depth;
27110       if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
27111       return *this;
27112     }
27113 
27114     //! Discard specified sequence of values in the image buffer, along a specific axis.
27115     /**
27116        \param values Sequence of values to discard.
27117        \param axis Axis along which the values are discarded. If set to \c 0 (default value)
27118          the method does it for all the buffer values and returns a one-column vector.
27119        \note Discarded values will change the image geometry, so the resulting image
27120        is returned as a one-column vector.
27121     **/
27122     template<typename t>
27123     CImg<T>& discard(const CImg<t>& values, const char axis=0) {
27124       if (is_empty() || !values) return *this;
27125       return get_discard(values,axis).move_to(*this);
27126     }
27127 
27128     template<typename t>
27129     CImg<T> get_discard(const CImg<t>& values, const char axis=0) const {
27130       CImg<T> res;
27131       if (!values) return +*this;
27132       if (is_empty()) return res;
27133       const ulongT vsiz = values.size();
27134       const char _axis = cimg::lowercase(axis);
27135       ulongT j = 0;
27136       unsigned int k = 0;
27137       int i0 = 0;
27138       res.assign(width(),height(),depth(),spectrum());
27139       switch (_axis) {
27140       case 'x' : {
27141         cimg_forX(*this,i) {
27142           if ((*this)(i)!=(T)values[j]) {
27143             if (j) --i;
27144             res.draw_image(k,get_columns(i0,i));
27145             k+=i - i0 + 1; i0 = i + 1; j = 0;
27146           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
27147         }
27148         if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; }
27149         res.resize(k,-100,-100,-100,0);
27150       } break;
27151       case 'y' : {
27152         cimg_forY(*this,i) {
27153           if ((*this)(0,i)!=(T)values[j]) {
27154             if (j) --i;
27155             res.draw_image(0,k,get_rows(i0,i));
27156             k+=i - i0 + 1; i0 = i + 1; j = 0;
27157           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
27158         }
27159         if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; }
27160         res.resize(-100,k,-100,-100,0);
27161       } break;
27162       case 'z' : {
27163         cimg_forZ(*this,i) {
27164           if ((*this)(0,0,i)!=(T)values[j]) {
27165             if (j) --i;
27166             res.draw_image(0,0,k,get_slices(i0,i));
27167             k+=i - i0 + 1; i0 = i + 1; j = 0;
27168           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
27169         }
27170         if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; }
27171         res.resize(-100,-100,k,-100,0);
27172       } break;
27173       case 'c' : {
27174         cimg_forC(*this,i) {
27175           if ((*this)(0,0,0,i)!=(T)values[j]) {
27176             if (j) --i;
27177             res.draw_image(0,0,0,k,get_channels(i0,i));
27178             k+=i - i0 + 1; i0 = i + 1; j = 0;
27179           } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
27180         }
27181         if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; }
27182         res.resize(-100,-100,-100,k,0);
27183       } break;
27184       default : {
27185         res.unroll('y');
27186         cimg_foroff(*this,i) {
27187           if ((*this)[i]!=(T)values[j]) {
27188             if (j) --i;
27189             std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T));
27190             k+=i - i0 + 1; i0 = (int)i + 1; j = 0;
27191           } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }}
27192         }
27193         const ulongT siz = size();
27194         if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; }
27195         res.resize(1,k,1,1,0);
27196       }
27197       }
27198       return res;
27199     }
27200 
27201     //! Discard neighboring duplicates in the image buffer, along the specified axis.
27202     CImg<T>& discard(const char axis=0) {
27203       return get_discard(axis).move_to(*this);
27204     }
27205 
27206     //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance.
27207     CImg<T> get_discard(const char axis=0) const {
27208       CImg<T> res;
27209       if (is_empty()) return res;
27210       const char _axis = cimg::lowercase(axis);
27211       T current = *_data?(T)0:(T)1;
27212       int j = 0;
27213       res.assign(width(),height(),depth(),spectrum());
27214       switch (_axis) {
27215       case 'x' : {
27216         cimg_forX(*this,i)
27217           if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); }
27218         res.resize(j,-100,-100,-100,0);
27219       } break;
27220       case 'y' : {
27221         cimg_forY(*this,i)
27222           if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); }
27223         res.resize(-100,j,-100,-100,0);
27224       } break;
27225       case 'z' : {
27226         cimg_forZ(*this,i)
27227           if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); }
27228         res.resize(-100,-100,j,-100,0);
27229       } break;
27230       case 'c' : {
27231         cimg_forC(*this,i)
27232           if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); }
27233         res.resize(-100,-100,-100,j,0);
27234       } break;
27235       default : {
27236         res.unroll('y');
27237         cimg_foroff(*this,i)
27238           if ((*this)[i]!=current) res[j++] = current = (*this)[i];
27239         res.resize(-100,j,-100,-100,0);
27240       }
27241       }
27242       return res;
27243     }
27244 
27245     //! Invert endianness of all pixel values.
27246     /**
27247      **/
27248     CImg<T>& invert_endianness() {
27249       cimg::invert_endianness(_data,size());
27250       return *this;
27251     }
27252 
27253     //! Invert endianness of all pixel values \newinstance.
27254     CImg<T> get_invert_endianness() const {
27255       return (+*this).invert_endianness();
27256     }
27257 
27258     //! Fill image with random values in specified range.
27259     /**
27260        \param val_min Minimal authorized random value.
27261        \param val_max Maximal authorized random value.
27262        \note Random variables are uniformely distributed in [val_min,val_max].
27263      **/
27264     CImg<T>& rand(const T& val_min, const T& val_max) {
27265       const float delta = (float)val_max - (float)val_min + (cimg::type<T>::is_float()?0:1);
27266       if (cimg::type<T>::is_float()) cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta);
27267       else cimg_for(*this,ptrd,T) *ptrd = std::min(val_max,(T)(val_min + cimg::rand()*delta));
27268       return *this;
27269     }
27270 
27271     //! Fill image with random values in specified range \newinstance.
27272     CImg<T> get_rand(const T& val_min, const T& val_max) const {
27273       return (+*this).rand(val_min,val_max);
27274     }
27275 
27276     //! Round pixel values.
27277     /**
27278        \param y Rounding precision.
27279        \param rounding_type Rounding type. Can be:
27280        - \c -1: Backward.
27281        - \c 0: Nearest.
27282        - \c 1: Forward.
27283     **/
27284     CImg<T>& round(const double y=1, const int rounding_type=0) {
27285       if (y>0)
27286         cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=8192))
27287         cimg_rof(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type);
27288       return *this;
27289     }
27290 
27291     //! Round pixel values \newinstance.
27292     CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
27293       return (+*this).round(y,rounding_type);
27294     }
27295 
27296     //! Add random noise to pixel values.
27297     /**
27298        \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the
27299          global value range.
27300        \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper,
27301          \p 3=Poisson or \p 4=Rician).
27302        \return A reference to the modified image instance.
27303        \note
27304        - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on
27305          the image value itself.
27306        - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
27307        \par Example
27308        \code
27309        const CImg<float> img("reference.jpg"), res = img.get_noise(40);
27310        (img,res.normalize(0,255)).display();
27311        \endcode
27312        \image html ref_noise.jpg
27313     **/
27314     CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
27315       if (is_empty()) return *this;
27316       const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
27317       Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
27318       if (nsigma==0 && noise_type!=3) return *this;
27319       if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
27320       if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.0);
27321       switch (noise_type) {
27322       case 0 : { // Gaussian noise
27323         cimg_rof(*this,ptrd,T) {
27324           Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand());
27325           if (val>vmax) val = vmax;
27326           if (val<vmin) val = vmin;
27327           *ptrd = (T)val;
27328         }
27329       } break;
27330       case 1 : { // Uniform noise
27331         cimg_rof(*this,ptrd,T) {
27332           Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::rand(-1,1));
27333           if (val>vmax) val = vmax;
27334           if (val<vmin) val = vmin;
27335           *ptrd = (T)val;
27336         }
27337       } break;
27338       case 2 : { // Salt & Pepper noise
27339         if (nsigma<0) nsigma = -nsigma;
27340         if (M==m) {
27341           if (cimg::type<T>::is_float()) { --m; ++M; }
27342           else { m = (Tfloat)cimg::type<T>::min(); M = (Tfloat)cimg::type<T>::max(); }
27343         }
27344         cimg_rof(*this,ptrd,T) if (cimg::rand(100)<nsigma) *ptrd = (T)(cimg::rand()<0.5?M:m);
27345       } break;
27346       case 3 : { // Poisson Noise
27347         cimg_rof(*this,ptrd,T) *ptrd = (T)cimg::prand(*ptrd);
27348       } break;
27349       case 4 : { // Rice noise
27350         const Tfloat sqrt2 = (Tfloat)std::sqrt(2.0);
27351         cimg_rof(*this,ptrd,T) {
27352           const Tfloat
27353             val0 = (Tfloat)*ptrd/sqrt2,
27354             re = (Tfloat)(val0 + nsigma*cimg::grand()),
27355             im = (Tfloat)(val0 + nsigma*cimg::grand());
27356           Tfloat val = cimg::hypot(re,im);
27357           if (val>vmax) val = vmax;
27358           if (val<vmin) val = vmin;
27359           *ptrd = (T)val;
27360         }
27361       } break;
27362       default :
27363         throw CImgArgumentException(_cimg_instance
27364                                     "noise(): Invalid specified noise type %d "
27365                                     "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
27366                                     cimg_instance,
27367                                     noise_type);
27368       }
27369       return *this;
27370     }
27371 
27372     //! Add random noise to pixel values \newinstance.
27373     CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
27374       return (+*this).noise(sigma,noise_type);
27375     }
27376 
27377     //! Linearly normalize pixel values.
27378     /**
27379        \param min_value Minimum desired value of the resulting image.
27380        \param max_value Maximum desired value of the resulting image.
27381        \par Example
27382        \code
27383        const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
27384        (img,res).display();
27385        \endcode
27386        \image html ref_normalize2.jpg
27387     **/
27388     CImg<T>& normalize(const T& min_value, const T& max_value) {
27389       if (is_empty()) return *this;
27390       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
27391       T m, M = max_min(m);
27392       const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
27393       if (m==M) return fill(min_value);
27394       if (m!=a || M!=b) cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a);
27395       return *this;
27396     }
27397 
27398     //! Linearly normalize pixel values \newinstance.
27399     CImg<Tfloat> get_normalize(const T& min_value, const T& max_value) const {
27400       return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value);
27401     }
27402 
27403     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
27404     /**
27405        \par Example
27406        \code
27407        const CImg<float> img("reference.jpg"), res = img.get_normalize();
27408        (img,res.normalize(0,255)).display();
27409        \endcode
27410        \image html ref_normalize.jpg
27411     **/
27412     CImg<T>& normalize() {
27413       const ulongT whd = (ulongT)_width*_height*_depth;
27414       cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27415       cimg_forYZ(*this,y,z) {
27416         T *ptrd = data(0,y,z,0);
27417         cimg_forX(*this,x) {
27418           const T *ptrs = ptrd;
27419           float n = 0;
27420           cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
27421           n = (float)std::sqrt(n);
27422           T *_ptrd = ptrd++;
27423           if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
27424           else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
27425         }
27426       }
27427       return *this;
27428     }
27429 
27430     //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance.
27431     CImg<Tfloat> get_normalize() const {
27432       return CImg<Tfloat>(*this,false).normalize();
27433     }
27434 
27435     //! Compute Lp-norm of each multi-valued pixel of the image instance.
27436     /**
27437        \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0).
27438        \par Example
27439        \code
27440        const CImg<float> img("reference.jpg"), res = img.get_norm();
27441        (img,res.normalize(0,255)).display();
27442        \endcode
27443        \image html ref_norm.jpg
27444     **/
27445     CImg<T>& norm(const int norm_type=2) {
27446       if (_spectrum==1 && norm_type) return abs();
27447       return get_norm(norm_type).move_to(*this);
27448     }
27449 
27450     //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance.
27451     CImg<Tfloat> get_norm(const int norm_type=2) const {
27452       if (is_empty()) return *this;
27453       if (_spectrum==1 && norm_type) return get_abs();
27454       const ulongT whd = (ulongT)_width*_height*_depth;
27455       CImg<Tfloat> res(_width,_height,_depth);
27456       switch (norm_type) {
27457       case -1 : { // Linf-norm.
27458         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27459         cimg_forYZ(*this,y,z) {
27460           const ulongT off = (ulongT)offset(0,y,z);
27461           const T *ptrs = _data + off;
27462           Tfloat *ptrd = res._data + off;
27463           cimg_forX(*this,x) {
27464             Tfloat n = 0;
27465             const T *_ptrs = ptrs++;
27466             cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
27467             *(ptrd++) = n;
27468           }
27469         }
27470       } break;
27471       case 0 : { // L0-norm.
27472         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27473         cimg_forYZ(*this,y,z) {
27474           const ulongT off = (ulongT)offset(0,y,z);
27475           const T *ptrs = _data + off;
27476           Tfloat *ptrd = res._data + off;
27477           cimg_forX(*this,x) {
27478             unsigned int n = 0;
27479             const T *_ptrs = ptrs++;
27480             cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; }
27481             *(ptrd++) = (Tfloat)n;
27482           }
27483         }
27484       } break;
27485       case 1 : { // L1-norm.
27486         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27487         cimg_forYZ(*this,y,z) {
27488           const ulongT off = (ulongT)offset(0,y,z);
27489           const T *ptrs = _data + off;
27490           Tfloat *ptrd = res._data + off;
27491           cimg_forX(*this,x) {
27492             Tfloat n = 0;
27493             const T *_ptrs = ptrs++;
27494             cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
27495             *(ptrd++) = n;
27496           }
27497         }
27498       } break;
27499       case 2 : { // L2-norm.
27500         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27501         cimg_forYZ(*this,y,z) {
27502           const ulongT off = (ulongT)offset(0,y,z);
27503           const T *ptrs = _data + off;
27504           Tfloat *ptrd = res._data + off;
27505           cimg_forX(*this,x) {
27506             Tfloat n = 0;
27507             const T *_ptrs = ptrs++;
27508             cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
27509             *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
27510           }
27511         }
27512       } break;
27513       default : { // Linf-norm.
27514         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16))
27515         cimg_forYZ(*this,y,z) {
27516           const ulongT off = (ulongT)offset(0,y,z);
27517           const T *ptrs = _data + off;
27518           Tfloat *ptrd = res._data + off;
27519           cimg_forX(*this,x) {
27520             Tfloat n = 0;
27521             const T *_ptrs = ptrs++;
27522             cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; }
27523             *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type);
27524           }
27525         }
27526       }
27527       }
27528       return res;
27529     }
27530 
27531     //! Cut pixel values in specified range.
27532     /**
27533        \param min_value Minimum desired value of the resulting image.
27534        \param max_value Maximum desired value of the resulting image.
27535        \par Example
27536        \code
27537        const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
27538        (img,res).display();
27539        \endcode
27540        \image html ref_cut.jpg
27541     **/
27542     CImg<T>& cut(const T& min_value, const T& max_value) {
27543       if (is_empty()) return *this;
27544       const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
27545       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27546       cimg_rof(*this,ptrd,T) *ptrd = (*ptrd<a)?a:((*ptrd>b)?b:*ptrd);
27547       return *this;
27548     }
27549 
27550     //! Cut pixel values in specified range \newinstance.
27551     CImg<T> get_cut(const T& min_value, const T& max_value) const {
27552       return (+*this).cut(min_value,max_value);
27553     }
27554 
27555     //! Uniformly quantize pixel values.
27556     /**
27557        \param nb_levels Number of quantization levels.
27558        \param keep_range Tells if resulting values keep the same range as the original ones.
27559        \par Example
27560        \code
27561        const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
27562        (img,res).display();
27563        \endcode
27564        \image html ref_quantize.jpg
27565     **/
27566     CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
27567       if (!nb_levels)
27568         throw CImgArgumentException(_cimg_instance
27569                                     "quantize(): Invalid quantization request with 0 values.",
27570                                     cimg_instance);
27571 
27572       if (is_empty()) return *this;
27573       Tfloat m, M = (Tfloat)max_min(m), range = M - m;
27574       if (range>0) {
27575         if (keep_range)
27576           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27577           cimg_rof(*this,ptrd,T) {
27578             const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
27579             *ptrd = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels);
27580           } else
27581           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27582           cimg_rof(*this,ptrd,T) {
27583             const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
27584             *ptrd = (T)std::min(val,nb_levels - 1);
27585           }
27586       }
27587       return *this;
27588     }
27589 
27590     //! Uniformly quantize pixel values \newinstance.
27591     CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
27592       return (+*this).quantize(n,keep_range);
27593     }
27594 
27595     //! Threshold pixel values.
27596     /**
27597        \param value Threshold value
27598        \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
27599        \param strict_threshold Tells if threshold value is strict.
27600        \par Example
27601        \code
27602        const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
27603        (img,res.normalize(0,255)).display();
27604        \endcode
27605        \image html ref_threshold.jpg
27606     **/
27607     CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) {
27608       if (is_empty()) return *this;
27609       if (strict_threshold) {
27610         if (soft_threshold)
27611           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27612           cimg_rof(*this,ptrd,T) {
27613             const T v = *ptrd;
27614             *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0;
27615           }
27616         else
27617           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
27618           cimg_rof(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0;
27619       } else {
27620         if (soft_threshold)
27621           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32768))
27622           cimg_rof(*this,ptrd,T) {
27623             const T v = *ptrd;
27624             *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0;
27625           }
27626         else
27627           cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=65536))
27628           cimg_rof(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0;
27629       }
27630       return *this;
27631     }
27632 
27633     //! Threshold pixel values \newinstance.
27634     CImg<T> get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const {
27635       return (+*this).threshold(value,soft_threshold,strict_threshold);
27636     }
27637 
27638     //! Compute the histogram of pixel values.
27639     /**
27640        \param nb_levels Number of desired histogram levels.
27641        \param min_value Minimum pixel value considered for the histogram computation.
27642          All pixel values lower than \p min_value will not be counted.
27643        \param max_value Maximum pixel value considered for the histogram computation.
27644          All pixel values higher than \p max_value will not be counted.
27645        \note
27646        - The histogram H of an image I is the 1d function where H(x) counts the number of occurences of the value x
27647          in the image I.
27648        - The resulting histogram is always defined in 1d. Histograms of multi-valued images are not multi-dimensional.
27649        \par Example
27650        \code
27651        const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
27652        img.display_graph(0,3);
27653        \endcode
27654        \image html ref_histogram.jpg
27655     **/
27656     CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) {
27657       return get_histogram(nb_levels,min_value,max_value).move_to(*this);
27658     }
27659 
27660     //! Compute the histogram of pixel values \overloading.
27661     CImg<T>& histogram(const unsigned int nb_levels) {
27662       return get_histogram(nb_levels).move_to(*this);
27663     }
27664 
27665     //! Compute the histogram of pixel values \newinstance.
27666     CImg<ulongT> get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const {
27667       if (!nb_levels || is_empty()) return CImg<ulongT>();
27668       const double
27669         vmin = (double)(min_value<max_value?min_value:max_value),
27670         vmax = (double)(min_value<max_value?max_value:min_value);
27671       CImg<ulongT> res(nb_levels,1,1,1,0);
27672       cimg_rof(*this,ptrs,T) {
27673         const T val = *ptrs;
27674         if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))];
27675       }
27676       return res;
27677     }
27678 
27679     //! Compute the histogram of pixel values \newinstance.
27680     CImg<ulongT> get_histogram(const unsigned int nb_levels) const {
27681       if (!nb_levels || is_empty()) return CImg<ulongT>();
27682       T vmax = 0, vmin = min_max(vmax);
27683       return get_histogram(nb_levels,vmin,vmax);
27684     }
27685 
27686     //! Equalize histogram of pixel values.
27687     /**
27688        \param nb_levels Number of histogram levels used for the equalization.
27689        \param min_value Minimum pixel value considered for the histogram computation.
27690          All pixel values lower than \p min_value will not be counted.
27691        \param max_value Maximum pixel value considered for the histogram computation.
27692          All pixel values higher than \p max_value will not be counted.
27693        \par Example
27694        \code
27695        const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
27696        (img,res).display();
27697        \endcode
27698        \image html ref_equalize.jpg
27699     **/
27700     CImg<T>& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) {
27701       if (!nb_levels || is_empty()) return *this;
27702       const T
27703         vmin = min_value<max_value?min_value:max_value,
27704         vmax = min_value<max_value?max_value:min_value;
27705       CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax);
27706       ulongT cumul = 0;
27707       cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
27708       if (!cumul) cumul = 1;
27709       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=1048576))
27710       cimg_rof(*this,ptrd,T) {
27711         const int pos = (int)((*ptrd-vmin)*(nb_levels - 1.)/(vmax-vmin));
27712         if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/cumul);
27713       }
27714       return *this;
27715     }
27716 
27717     //! Equalize histogram of pixel values \overloading.
27718     CImg<T>& equalize(const unsigned int nb_levels) {
27719       if (!nb_levels || is_empty()) return *this;
27720       T vmax = 0, vmin = min_max(vmax);
27721       return equalize(nb_levels,vmin,vmax);
27722     }
27723 
27724     //! Equalize histogram of pixel values \newinstance.
27725     CImg<T> get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const {
27726       return (+*this).equalize(nblevels,val_min,val_max);
27727     }
27728 
27729     //! Equalize histogram of pixel values \newinstance.
27730     CImg<T> get_equalize(const unsigned int nblevels) const {
27731       return (+*this).equalize(nblevels);
27732     }
27733 
27734     //! Index multi-valued pixels regarding to a specified colormap.
27735     /**
27736        \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing.
27737        \param dithering Level of dithering (0=disable, 1=standard level).
27738        \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors.
27739        \note
27740        - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>.
27741        \par Example
27742        \code
27743        const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
27744        const CImg<float> res = img.get_index(colormap,1,true);
27745        (img,res).display();
27746        \endcode
27747        \image html ref_index.jpg
27748     **/
27749     template<typename t>
27750     CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
27751       return get_index(colormap,dithering,map_indexes).move_to(*this);
27752     }
27753 
27754     //! Index multi-valued pixels regarding to a specified colormap \newinstance.
27755     template<typename t>
27756     CImg<typename CImg<t>::Tuint>
27757     get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
27758       if (colormap._spectrum!=_spectrum)
27759         throw CImgArgumentException(_cimg_instance
27760                                     "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
27761                                     "have incompatible dimensions.",
27762                                     cimg_instance,
27763                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
27764 
27765       typedef typename CImg<t>::Tuint tuint;
27766       if (is_empty()) return CImg<tuint>();
27767       const ulongT
27768         whd = (ulongT)_width*_height*_depth,
27769         pwhd = (ulongT)colormap._width*colormap._height*colormap._depth;
27770       CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
27771       tuint *ptrd = res._data;
27772       if (dithering>0) { // Dithered versions.
27773         const float ndithering = cimg::cut(dithering,0,1)/16;
27774         Tfloat valm = 0, valM = (Tfloat)max_min(valm);
27775         if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
27776         CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1);
27777         Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
27778         const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth;
27779         switch (_spectrum) {
27780         case 1 : { // Optimized for scalars.
27781           cimg_forYZ(*this,y,z) {
27782             if (y<height() - 2) {
27783               Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y + 1,z,0);
27784               cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
27785             }
27786             Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
27787             cimg_forX(*this,x) {
27788               const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
27789               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27790               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
27791                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
27792                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27793               }
27794               const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
27795               *ptrs0+=7*err0; *(ptrsn0 - 1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
27796               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27797             }
27798             cimg::swap(cache_current,cache_next);
27799           }
27800         } break;
27801         case 2 : { // Optimized for 2d vectors.
27802           tuint *ptrd1 = ptrd + whd;
27803           cimg_forYZ(*this,y,z) {
27804             if (y<height() - 2) {
27805               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
27806               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd;
27807               cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
27808             }
27809             Tfloat
27810               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
27811               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
27812             cimg_forX(*this,x) {
27813               const Tfloat
27814                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
27815                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
27816               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27817               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
27818                 const Tfloat
27819                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
27820                   dist = pval0*pval0 + pval1*pval1;
27821                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27822               }
27823               const t *const ptrmin1 = ptrmin0 + pwhd;
27824               const Tfloat
27825                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
27826                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
27827               *ptrs0+=7*err0; *ptrs1+=7*err1;
27828               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1;
27829               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
27830               *ptrsn0+=err0; *ptrsn1+=err1;
27831               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
27832               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27833             }
27834             cimg::swap(cache_current,cache_next);
27835           }
27836         } break;
27837         case 3 : { // Optimized for 3d vectors (colors).
27838           tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
27839           cimg_forYZ(*this,y,z) {
27840             if (y<height() - 2) {
27841               Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
27842               const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
27843               cimg_forX(*this,x) {
27844                 *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++);
27845               }
27846             }
27847             Tfloat
27848               *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
27849               *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
27850             cimg_forX(*this,x) {
27851               const Tfloat
27852                 _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
27853                 _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
27854                 _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
27855               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27856               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
27857                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
27858                 const Tfloat
27859                   pval0 = (Tfloat)*(ptrp0++) - val0,
27860                   pval1 = (Tfloat)*(ptrp1++) - val1,
27861                   pval2 = (Tfloat)*(ptrp2++) - val2,
27862                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
27863                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27864               }
27865               const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
27866               const Tfloat
27867                 err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
27868                 err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
27869                 err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
27870 
27871               *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
27872               *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; *(ptrsn2 - 1)+=3*err2;
27873               *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
27874               *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
27875 
27876               if (map_indexes) {
27877                 *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2;
27878               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27879             }
27880             cimg::swap(cache_current,cache_next);
27881           }
27882         } break;
27883         default : // Generic version
27884           cimg_forYZ(*this,y,z) {
27885             if (y<height() - 2) {
27886               Tfloat *ptrc = cache_next;
27887               cimg_forC(*this,c) {
27888                 Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y + 1,z,c);
27889                 cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
27890                 ptrc+=cwhd;
27891               }
27892             }
27893             Tfloat *ptrs = cache_current, *ptrsn = cache_next;
27894             cimg_forX(*this,x) {
27895               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
27896               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
27897                 Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
27898                 cimg_forC(*this,c) {
27899                   const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
27900                   dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
27901                 }
27902                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
27903               }
27904               const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++) - 1;
27905               cimg_forC(*this,c) {
27906                 const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
27907                 *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
27908                 _ptrmin+=pwhd; _ptrs+=cwhd - 1; _ptrsn+=cwhd - 2;
27909               }
27910               if (map_indexes) {
27911                 tuint *_ptrd = ptrd++;
27912                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
27913               }
27914               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
27915             }
27916             cimg::swap(cache_current,cache_next);
27917           }
27918         }
27919       } else { // Non-dithered versions
27920         switch (_spectrum) {
27921         case 1 : { // Optimized for scalars.
27922           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
27923           cimg_forYZ(*this,y,z) {
27924             tuint *ptrd = res.data(0,y,z);
27925             for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
27926               const Tfloat val0 = (Tfloat)*(ptrs0++);
27927               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27928               for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
27929                 const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
27930                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27931               }
27932               if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27933             }
27934           }
27935         } break;
27936         case 2 : { // Optimized for 2d vectors.
27937           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
27938           cimg_forYZ(*this,y,z) {
27939             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd;
27940             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
27941               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
27942               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27943               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
27944                 const Tfloat
27945                   pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
27946                   dist = pval0*pval0 + pval1*pval1;
27947                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27948               }
27949               if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
27950               else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27951             }
27952           }
27953         } break;
27954         case 3 : { // Optimized for 3d vectors (colors).
27955           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
27956           cimg_forYZ(*this,y,z) {
27957             tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
27958             for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd,
27959                    *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
27960               const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
27961               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
27962               for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
27963                      *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
27964                 const Tfloat
27965                   pval0 = (Tfloat)*(ptrp0++) - val0,
27966                   pval1 = (Tfloat)*(ptrp1++) - val1,
27967                   pval2 = (Tfloat)*(ptrp2++) - val2,
27968                   dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
27969                 if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
27970               }
27971               if (map_indexes) {
27972                 *(ptrd++) = (tuint)*ptrmin0;
27973                 *(ptrd1++) = (tuint)*(ptrmin0 + pwhd);
27974                 *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd);
27975               } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
27976             }
27977           }
27978         } break;
27979         default : // Generic version.
27980           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=64 && _height*_depth>=16 && pwhd>=16))
27981           cimg_forYZ(*this,y,z) {
27982             tuint *ptrd = res.data(0,y,z);
27983             for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) {
27984               Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
27985               for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
27986                 Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
27987                 cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
27988                 if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
27989               }
27990               if (map_indexes) {
27991                 tuint *_ptrd = ptrd++;
27992                 cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
27993               }
27994               else *(ptrd++) = (tuint)(ptrmin - colormap._data);
27995             }
27996           }
27997         }
27998       }
27999       return res;
28000     }
28001 
28002     //! Map predefined colormap on the scalar (indexed) image instance.
28003     /**
28004        \param colormap Multi-valued colormap used for mapping the indexes.
28005        \param boundary_conditions The border condition type { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
28006        \par Example
28007        \code
28008        const CImg<float> img("reference.jpg"),
28009                          colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
28010                          colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
28011                          res = img.get_index(colormap1,0).map(colormap2);
28012        (img,res).display();
28013        \endcode
28014        \image html ref_map.jpg
28015     **/
28016     template<typename t>
28017     CImg<T>& map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) {
28018       return get_map(colormap,boundary_conditions).move_to(*this);
28019     }
28020 
28021     //! Map predefined colormap on the scalar (indexed) image instance \newinstance.
28022     template<typename t>
28023     CImg<t> get_map(const CImg<t>& colormap, const unsigned int boundary_conditions=0) const {
28024       if (_spectrum!=1 && colormap._spectrum!=1)
28025         throw CImgArgumentException(_cimg_instance
28026                                     "map(): Instance and specified colormap (%u,%u,%u,%u,%p) "
28027                                     "have incompatible dimensions.",
28028                                     cimg_instance,
28029                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
28030 
28031       const ulongT
28032         whd = (ulongT)_width*_height*_depth,
28033         cwhd = (ulongT)colormap._width*colormap._height*colormap._depth,
28034         cwhd2 = 2*cwhd;
28035       CImg<t> res(_width,_height,_depth,colormap._spectrum==1?_spectrum:colormap._spectrum);
28036       switch (colormap._spectrum) {
28037 
28038       case 1 : { // Optimized for scalars
28039         const T *ptrs = _data;
28040         switch (boundary_conditions) {
28041         case 3 : // Mirror
28042           cimg_for(res,ptrd,t) {
28043             const ulongT ind = ((ulongT)*(ptrs++))%cwhd2;
28044             *ptrd = colormap[ind<cwhd?ind:cwhd2 - ind - 1];
28045           }
28046           break;
28047         case 2 : // Periodic
28048           cimg_for(res,ptrd,t) {
28049             const ulongT ind = (ulongT)*(ptrs++);
28050             *ptrd = colormap[ind%cwhd];
28051           } break;
28052         case 1 : // Neumann
28053           cimg_for(res,ptrd,t) {
28054             const longT ind = (longT)*(ptrs++);
28055             *ptrd = colormap[cimg::cut(ind,(longT)0,(longT)cwhd - 1)];
28056           } break;
28057         default : // Dirichlet
28058           cimg_for(res,ptrd,t) {
28059             const ulongT ind = (ulongT)*(ptrs++);
28060             *ptrd = ind<cwhd?colormap[ind]:(t)0;
28061           }
28062         }
28063       } break;
28064 
28065       case 2 : { // Optimized for 2d vectors.
28066         const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd;
28067         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
28068         switch (boundary_conditions) {
28069         case 3 : // Mirror
28070           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28071             const ulongT
28072               _ind = ((ulongT)*(ptrs++))%cwhd2,
28073               ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
28074             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
28075           }
28076           break;
28077         case 2 : // Periodic
28078           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28079             const ulongT ind = ((ulongT)*(ptrs++))%cwhd;
28080             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
28081           }
28082           break;
28083         case 1 : // Neumann
28084           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28085             const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1);
28086             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
28087           }
28088           break;
28089         default : // Dirichlet
28090           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28091             const ulongT ind = (ulongT)*(ptrs++);
28092             const bool is_in = ind<cwhd;
28093             *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0;
28094           }
28095         }
28096       } break;
28097 
28098       case 3 : { // Optimized for 3d vectors (colors).
28099         const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + cwhd, *ptrp2 = ptrp1 + cwhd;
28100         t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
28101         switch (boundary_conditions) {
28102         case 3 : // Mirror
28103           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28104             const ulongT
28105               _ind = ((ulongT)*(ptrs++))%cwhd2,
28106               ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
28107             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
28108           } break;
28109         case 2 : // Periodic
28110           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28111             const ulongT ind = ((ulongT)*(ptrs++))%cwhd;
28112             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
28113           } break;
28114         case 1 : // Neumann
28115           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28116             const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1);
28117             *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
28118           } break;
28119         default : // Dirichlet
28120           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28121             const ulongT ind = (ulongT)*(ptrs++);
28122             const bool is_in = ind<cwhd;
28123             *(ptrd0++) = is_in?ptrp0[ind]:(t)0; *(ptrd1++) = is_in?ptrp1[ind]:(t)0; *(ptrd2++) = is_in?ptrp2[ind]:(t)0;
28124           }
28125         }
28126       } break;
28127 
28128       default : { // Generic version.
28129         t *ptrd = res._data;
28130         switch (boundary_conditions) {
28131         case 3 : // Mirror
28132           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28133             const ulongT
28134               _ind = ((ulongT)*(ptrs++))%cwhd,
28135               ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
28136             const t *ptrp = colormap._data + ind;
28137             t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
28138           } break;
28139         case 2 : // Periodic
28140           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28141             const ulongT ind = ((ulongT)*(ptrs++))%cwhd;
28142             const t *ptrp = colormap._data + ind;
28143             t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
28144           } break;
28145         case 1 : // Neumann
28146           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28147             const longT ind = cimg::cut((longT)*(ptrs++),(longT)0,(longT)cwhd - 1);
28148             const t *ptrp = colormap._data + ind;
28149             t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
28150           } break;
28151         default : // Dirichlet
28152           for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
28153             const ulongT ind = (ulongT)*(ptrs++);
28154             const bool is_in = ind<cwhd;
28155             if (is_in) {
28156               const t *ptrp = colormap._data + ind;
28157               t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=cwhd; }
28158             } else {
28159               t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = (t)0; _ptrd+=whd; }
28160             }
28161           }
28162         }
28163       }
28164       }
28165       return res;
28166     }
28167 
28168     //! Label connected components.
28169     /**
28170        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
28171        in 2d case, and between 6(false)- or 26(true)-connectivity in 3d case.
28172        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
28173        \note The algorithm of connected components computation has been primarily done
28174        by A. Meijster, according to the publication:
28175        'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
28176        In: Science of Computer Programming 41 (2001), pp. 173--194'.
28177        The submitted code has then been modified to fit CImg coding style and constraints.
28178     **/
28179     CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) {
28180       return get_label(is_high_connectivity,tolerance).move_to(*this);
28181     }
28182 
28183     //! Label connected components \newinstance.
28184     CImg<ulongT> get_label(const bool is_high_connectivity=false,
28185                            const Tfloat tolerance=0) const {
28186       if (is_empty()) return CImg<ulongT>();
28187 
28188       // Create neighborhood tables.
28189       int dx[13], dy[13], dz[13], nb = 0;
28190       dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0;
28191       dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0;
28192       if (is_high_connectivity) {
28193         dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0;
28194         dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0;
28195       }
28196       if (_depth>1) { // 3d version.
28197         dx[nb] = 0; dy[nb] = 0; dz[nb++]=1;
28198         if (is_high_connectivity) {
28199           dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1;
28200           dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1;
28201           dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1;
28202           dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1;
28203 
28204           dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1;
28205           dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1;
28206           dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1;
28207           dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1;
28208         }
28209       }
28210       return _label(nb,dx,dy,dz,tolerance);
28211     }
28212 
28213     //! Label connected components \overloading.
28214     /**
28215        \param connectivity_mask Mask of the neighboring pixels.
28216        \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
28217     **/
28218     template<typename t>
28219     CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0) {
28220       return get_label(connectivity_mask,tolerance).move_to(*this);
28221     }
28222 
28223     //! Label connected components \newinstance.
28224     template<typename t>
28225     CImg<ulongT> get_label(const CImg<t>& connectivity_mask,
28226                            const Tfloat tolerance=0) const {
28227       int nb = 0;
28228       cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
28229       CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
28230       nb = 0;
28231       cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
28232                                                connectivity_mask(x,y,z)) {
28233         dx[nb] = x; dy[nb] = y; dz[nb++] = z;
28234       }
28235       return _label(nb,dx,dy,dz,tolerance);
28236     }
28237 
28238     CImg<ulongT> _label(const unsigned int nb, const int *const dx,
28239                         const int *const dy, const int *const dz,
28240                         const Tfloat tolerance) const {
28241       CImg<ulongT> res(_width,_height,_depth,_spectrum);
28242       cimg_forC(*this,c) {
28243         CImg<ulongT> _res = res.get_shared_channel(c);
28244 
28245         // Init label numbers.
28246         ulongT *ptr = _res.data();
28247         cimg_foroff(_res,p) *(ptr++) = p;
28248 
28249         // For each neighbour-direction, label.
28250         for (unsigned int n = 0; n<nb; ++n) {
28251           const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
28252           if (_dx || _dy || _dz) {
28253             const int
28254               x0 = _dx<0?-_dx:0,
28255               x1 = _dx<0?width():width() - _dx,
28256               y0 = _dy<0?-_dy:0,
28257               y1 = _dy<0?height():height() - _dy,
28258               z0 = _dz<0?-_dz:0,
28259               z1 = _dz<0?depth():depth() - _dz;
28260             const longT
28261               wh = (longT)width()*height(),
28262               whd = (longT)width()*height()*depth(),
28263               offset = _dz*wh + _dy*width() + _dx;
28264             for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
28265               for (longT y = y0, ny = y0 + _dy, py = y0*width() + pz; y<y1; ++y, ++ny, py+=width()) {
28266                 for (longT x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
28267                   if (cimg::abs((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd))<=tolerance) {
28268                     const longT q = p + offset;
28269                     ulongT x, y;
28270                     for (x = (ulongT)(p<q?q:p), y = (ulongT)(p<q?p:q); x!=y && _res[x]!=x; ) {
28271                       x = _res[x]; if (x<y) cimg::swap(x,y);
28272                     }
28273                     if (x!=y) _res[x] = (ulongT)y;
28274                     for (ulongT _p = (ulongT)p; _p!=y; ) {
28275                       const ulongT h = _res[_p];
28276                       _res[_p] = (ulongT)y;
28277                       _p = h;
28278                     }
28279                     for (ulongT _q = (ulongT)q; _q!=y; ) {
28280                       const ulongT h = _res[_q];
28281                       _res[_q] = (ulongT)y;
28282                       _q = h;
28283                     }
28284                   }
28285                 }
28286               }
28287             }
28288           }
28289         }
28290 
28291         // Resolve equivalences.
28292         ulongT counter = 0;
28293         ptr = _res.data();
28294         cimg_foroff(_res,p) { *ptr = *ptr==p?counter++:_res[*ptr]; ++ptr; }
28295       }
28296       return res;
28297     }
28298 
28299     // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version.
28300     CImg<T>& _system_strescape() {
28301 #define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\
28302       move_to(list); \
28303       CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break
28304       CImgList<T> list;
28305       const T *ptrs = _data;
28306       cimg_for(*this,p,T) switch ((int)*p) {
28307         cimg_system_strescape('\\',"\\\\");
28308         cimg_system_strescape('\"',"\\\"");
28309         cimg_system_strescape('!',"\"\\!\"");
28310         cimg_system_strescape('`',"\\`");
28311         cimg_system_strescape('$',"\\$");
28312       }
28313       if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list);
28314       return (list>'x').move_to(*this);
28315     }
28316 
28317     //@}
28318     //---------------------------------
28319     //
28320     //! \name Color Base Management
28321     //@{
28322     //---------------------------------
28323 
28324     //! Return colormap \e "default", containing 256 colors entries in RGB.
28325     /**
28326        \return The following \c 256x1x1x3 colormap is returned:
28327        \image html ref_colormap_default.jpg
28328     **/
28329     static const CImg<Tuchar>& default_LUT256() {
28330       static CImg<Tuchar> colormap;
28331       cimg::mutex(8);
28332       if (!colormap) {
28333         colormap.assign(1,256,1,3);
28334         for (unsigned int index = 0, r = 16; r<256; r+=32)
28335           for (unsigned int g = 16; g<256; g+=32)
28336             for (unsigned int b = 32; b<256; b+=64) {
28337               colormap(0,index,0) = (Tuchar)r;
28338               colormap(0,index,1) = (Tuchar)g;
28339               colormap(0,index++,2) = (Tuchar)b;
28340             }
28341       }
28342       cimg::mutex(8,0);
28343       return colormap;
28344     }
28345 
28346     //! Return colormap \e "HSV", containing 256 colors entries in RGB.
28347     /**
28348        \return The following \c 256x1x1x3 colormap is returned:
28349        \image html ref_colormap_hsv.jpg
28350     **/
28351     static const CImg<Tuchar>& HSV_LUT256() {
28352       static CImg<Tuchar> colormap;
28353       cimg::mutex(8);
28354       if (!colormap) {
28355         CImg<Tint> tmp(1,256,1,3,1);
28356         tmp.get_shared_channel(0).sequence(0,359);
28357         colormap = tmp.HSVtoRGB();
28358       }
28359       cimg::mutex(8,0);
28360       return colormap;
28361     }
28362 
28363     //! Return colormap \e "lines", containing 256 colors entries in RGB.
28364     /**
28365        \return The following \c 256x1x1x3 colormap is returned:
28366        \image html ref_colormap_lines.jpg
28367     **/
28368     static const CImg<Tuchar>& lines_LUT256() {
28369       static const unsigned char pal[] = {
28370         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,
28371         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,
28372         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,
28373         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,
28374         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,
28375         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,
28376         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,
28377         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,
28378         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,
28379         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,
28380         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,
28381         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,
28382         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,
28383         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,
28384         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,
28385         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,
28386         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,
28387         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,
28388         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,
28389         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,
28390         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,
28391         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,
28392         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,
28393         23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 };
28394       static const CImg<Tuchar> colormap(pal,1,256,1,3,false);
28395       return colormap;
28396     }
28397 
28398     //! Return colormap \e "hot", containing 256 colors entries in RGB.
28399     /**
28400        \return The following \c 256x1x1x3 colormap is returned:
28401        \image html ref_colormap_hot.jpg
28402     **/
28403     static const CImg<Tuchar>& hot_LUT256() {
28404       static CImg<Tuchar> colormap;
28405       cimg::mutex(8);
28406       if (!colormap) {
28407         colormap.assign(1,4,1,3,(T)0);
28408         colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255;
28409         colormap.resize(1,256,1,3,3);
28410       }
28411       cimg::mutex(8,0);
28412       return colormap;
28413     }
28414 
28415     //! Return colormap \e "cool", containing 256 colors entries in RGB.
28416     /**
28417        \return The following \c 256x1x1x3 colormap is returned:
28418        \image html ref_colormap_cool.jpg
28419     **/
28420     static const CImg<Tuchar>& cool_LUT256() {
28421       static CImg<Tuchar> colormap;
28422       cimg::mutex(8);
28423       if (!colormap) colormap.assign(1,2,1,3).fill((T)0,(T)255,(T)255,(T)0,(T)255,(T)255).resize(1,256,1,3,3);
28424       cimg::mutex(8,0);
28425       return colormap;
28426     }
28427 
28428     //! Return colormap \e "jet", containing 256 colors entries in RGB.
28429     /**
28430        \return The following \c 256x1x1x3 colormap is returned:
28431        \image html ref_colormap_jet.jpg
28432     **/
28433     static const CImg<Tuchar>& jet_LUT256() {
28434       static CImg<Tuchar> colormap;
28435       cimg::mutex(8);
28436       if (!colormap) {
28437         colormap.assign(1,4,1,3,(T)0);
28438         colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255;
28439         colormap.resize(1,256,1,3,3);
28440       }
28441       cimg::mutex(8,0);
28442       return colormap;
28443     }
28444 
28445     //! Return colormap \e "flag", containing 256 colors entries in RGB.
28446     /**
28447        \return The following \c 256x1x1x3 colormap is returned:
28448        \image html ref_colormap_flag.jpg
28449     **/
28450     static const CImg<Tuchar>& flag_LUT256() {
28451       static CImg<Tuchar> colormap;
28452       cimg::mutex(8);
28453       if (!colormap) {
28454         colormap.assign(1,4,1,3,(T)0);
28455         colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
28456         colormap.resize(1,256,1,3,0,2);
28457       }
28458       cimg::mutex(8,0);
28459       return colormap;
28460     }
28461 
28462     //! Return colormap \e "cube", containing 256 colors entries in RGB.
28463     /**
28464        \return The following \c 256x1x1x3 colormap is returned:
28465        \image html ref_colormap_cube.jpg
28466     **/
28467     static const CImg<Tuchar>& cube_LUT256() {
28468       static CImg<Tuchar> colormap;
28469       cimg::mutex(8);
28470       if (!colormap) {
28471         colormap.assign(1,8,1,3,(T)0);
28472         colormap[1] = colormap[3] = colormap[5] = colormap[7] =
28473           colormap[10] = colormap[11] = colormap[12] = colormap[13] =
28474           colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255;
28475         colormap.resize(1,256,1,3,3);
28476       }
28477       cimg::mutex(8,0);
28478       return colormap;
28479     }
28480 
28481     //! Convert pixel values from sRGB to RGB color spaces.
28482     CImg<T>& sRGBtoRGB() {
28483       if (is_empty()) return *this;
28484       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32))
28485       cimg_rof(*this,ptr,T) {
28486         const Tfloat
28487           sval = (Tfloat)*ptr/255,
28488           val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f));
28489         *ptr = (T)cimg::cut(val*255,0,255);
28490       }
28491       return *this;
28492     }
28493 
28494     //! Convert pixel values from sRGB to RGB color spaces \newinstance.
28495     CImg<Tfloat> get_sRGBtoRGB() const {
28496       return CImg<Tfloat>(*this,false).sRGBtoRGB();
28497     }
28498 
28499     //! Convert pixel values from RGB to sRGB color spaces.
28500     CImg<T>& RGBtosRGB() {
28501       if (is_empty()) return *this;
28502       cimg_pragma_openmp(parallel for cimg_openmp_if(size()>=32))
28503       cimg_rof(*this,ptr,T) {
28504         const Tfloat
28505           val = (Tfloat)*ptr/255,
28506           sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f);
28507         *ptr = (T)cimg::cut(sval*255,0,255);
28508       }
28509       return *this;
28510     }
28511 
28512     //! Convert pixel values from RGB to sRGB color spaces \newinstance.
28513     CImg<Tfloat> get_RGBtosRGB() const {
28514       return CImg<Tfloat>(*this,false).RGBtosRGB();
28515     }
28516 
28517     //! Convert pixel values from RGB to HSI color spaces.
28518     CImg<T>& RGBtoHSI() {
28519       if (_spectrum!=3)
28520         throw CImgInstanceException(_cimg_instance
28521                                     "RGBtoHSI(): Instance is not a RGB image.",
28522                                     cimg_instance);
28523 
28524       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28525       const ulongT whd = (ulongT)_width*_height*_depth;
28526       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28527       for (ulongT N = 0; N<whd; ++N) {
28528         const Tfloat
28529           R = (Tfloat)p1[N],
28530           G = (Tfloat)p2[N],
28531           B = (Tfloat)p3[N],
28532           theta = (Tfloat)(std::acos(0.5f*((R - G) + (R - B))/
28533                                      std::sqrt(cimg::sqr(R - G) + (R - B)*(G - B)))*180/cimg::PI),
28534           m = cimg::min(R,G,B),
28535           sum = R + G + B;
28536         Tfloat H = 0, S = 0, I = 0;
28537         if (theta>0) H = B<=G?theta:360 - theta;
28538         if (sum>0) S = 1 - 3*m/sum;
28539         I = sum/(3*255);
28540         p1[N] = (T)cimg::cut(H,0,360);
28541         p2[N] = (T)cimg::cut(S,0,1);
28542         p3[N] = (T)cimg::cut(I,0,1);
28543       }
28544       return *this;
28545     }
28546 
28547     //! Convert pixel values from RGB to HSI color spaces \newinstance.
28548     CImg<Tfloat> get_RGBtoHSI() const {
28549       return CImg<Tfloat>(*this,false).RGBtoHSI();
28550     }
28551 
28552     //! Convert pixel values from HSI to RGB color spaces.
28553     CImg<T>& HSItoRGB() {
28554       if (_spectrum!=3)
28555         throw CImgInstanceException(_cimg_instance
28556                                     "HSItoRGB(): Instance is not a HSI image.",
28557                                     cimg_instance);
28558 
28559       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28560       const ulongT whd = (ulongT)_width*_height*_depth;
28561       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28562       for (ulongT N = 0; N<whd; ++N) {
28563         Tfloat
28564           H = cimg::mod((Tfloat)p1[N],(Tfloat)360),
28565           S = (Tfloat)p2[N],
28566           I = (Tfloat)p3[N],
28567           a = I*(1 - S),
28568           R = 0, G = 0, B = 0;
28569         if (H<120) {
28570           B = a;
28571           R = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180)));
28572           G = 3*I - (R + B);
28573         } else if (H<240) {
28574           H-=120;
28575           R = a;
28576           G = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180)));
28577           B = 3*I - (R + G);
28578         } else {
28579           H-=240;
28580           G = a;
28581           B = (Tfloat)(I*(1 + S*std::cos(H*cimg::PI/180)/std::cos((60 - H)*cimg::PI/180)));
28582           R = 3*I - (G + B);
28583         }
28584         p1[N] = (T)cimg::cut(R*255,0,255);
28585         p2[N] = (T)cimg::cut(G*255,0,255);
28586         p3[N] = (T)cimg::cut(B*255,0,255);
28587       }
28588       return *this;
28589     }
28590 
28591     //! Convert pixel values from HSI to RGB color spaces \newinstance.
28592     CImg<Tfloat> get_HSItoRGB() const {
28593       return CImg< Tuchar>(*this,false).HSItoRGB();
28594     }
28595 
28596     //! Convert pixel values from RGB to HSL color spaces.
28597     CImg<T>& RGBtoHSL() {
28598       if (_spectrum!=3)
28599         throw CImgInstanceException(_cimg_instance
28600                                     "RGBtoHSL(): Instance is not a RGB image.",
28601                                     cimg_instance);
28602 
28603       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28604       const ulongT whd = (ulongT)_width*_height*_depth;
28605       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28606       for (ulongT N = 0; N<whd; ++N) {
28607         const Tfloat
28608           R = (Tfloat)p1[N],
28609           G = (Tfloat)p2[N],
28610           B = (Tfloat)p3[N],
28611           m = cimg::min(R,G,B),
28612           M = cimg::max(R,G,B),
28613           L = (m + M)/(2*255);
28614         Tfloat H = 0, S = 0;
28615         if (M!=m) {
28616           const Tfloat
28617             f = R==m?G - B:G==m?B - R:R - G,
28618             i = R==m?3:G==m?5:1;
28619           H = i - f/(M - m);
28620           if (H>=6) H-=6;
28621           H*=60;
28622           S = 2*L<=1?(M - m)/(M + m):(M - m)/(2*255 - M - m);
28623         }
28624         p1[N] = (T)cimg::cut(H,0,360);
28625         p2[N] = (T)cimg::cut(S,0,1);
28626         p3[N] = (T)cimg::cut(L,0,1);
28627       }
28628       return *this;
28629     }
28630 
28631     //! Convert pixel values from RGB to HSL color spaces \newinstance.
28632     CImg<Tfloat> get_RGBtoHSL() const {
28633       return CImg<Tfloat>(*this,false).RGBtoHSL();
28634     }
28635 
28636     //! Convert pixel values from HSL to RGB color spaces.
28637     CImg<T>& HSLtoRGB() {
28638       if (_spectrum!=3)
28639         throw CImgInstanceException(_cimg_instance
28640                                     "HSLtoRGB(): Instance is not a HSL image.",
28641                                     cimg_instance);
28642 
28643       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28644       const ulongT whd = (ulongT)_width*_height*_depth;
28645       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28646       for (ulongT N = 0; N<whd; ++N) {
28647         const Tfloat
28648           H = cimg::mod((Tfloat)p1[N],(Tfloat)360),
28649           S = (Tfloat)p2[N],
28650           L = (Tfloat)p3[N],
28651           q = 2*L<1?L*(1 + S):L + S - L*S,
28652           p = 2*L - q,
28653           h = H/360,
28654           tr = h + (Tfloat)1/3,
28655           tg = h,
28656           tb = h - (Tfloat)1/3,
28657           ntr = tr<0?tr + 1:tr>1?tr - 1:(Tfloat)tr,
28658           ntg = tg<0?tg + 1:tg>1?tg - 1:(Tfloat)tg,
28659           ntb = tb<0?tb + 1:tb>1?tb - 1:(Tfloat)tb,
28660           R = 6*ntr<1?p + (q - p)*6*ntr:2*ntr<1?q:3*ntr<2?p + (q - p)*6*(2.0f/3 - ntr):p,
28661           G = 6*ntg<1?p + (q - p)*6*ntg:2*ntg<1?q:3*ntg<2?p + (q - p)*6*(2.0f/3 - ntg):p,
28662           B = 6*ntb<1?p + (q - p)*6*ntb:2*ntb<1?q:3*ntb<2?p + (q - p)*6*(2.0f/3 - ntb):p;
28663         p1[N] = (T)cimg::cut(255*R,0,255);
28664         p2[N] = (T)cimg::cut(255*G,0,255);
28665         p3[N] = (T)cimg::cut(255*B,0,255);
28666       }
28667       return *this;
28668     }
28669 
28670     //! Convert pixel values from HSL to RGB color spaces \newinstance.
28671     CImg<Tuchar> get_HSLtoRGB() const {
28672       return CImg<Tuchar>(*this,false).HSLtoRGB();
28673     }
28674 
28675     //! Convert pixel values from RGB to HSV color spaces.
28676     CImg<T>& RGBtoHSV() {
28677       if (_spectrum!=3)
28678         throw CImgInstanceException(_cimg_instance
28679                                     "RGBtoHSV(): Instance is not a RGB image.",
28680                                     cimg_instance);
28681 
28682       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28683       const ulongT whd = (ulongT)_width*_height*_depth;
28684       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28685       for (ulongT N = 0; N<whd; ++N) {
28686         const Tfloat
28687           R = (Tfloat)p1[N],
28688           G = (Tfloat)p2[N],
28689           B = (Tfloat)p3[N],
28690           m = cimg::min(R,G,B),
28691           M = cimg::max(R,G,B);
28692         Tfloat H = 0, S = 0;
28693         if (M!=m) {
28694           const Tfloat
28695             f = R==m?G - B:G==m?B - R:R - G,
28696             i = R==m?3:G==m?5:1;
28697           H = i - f/(M - m);
28698           if (H>=6) H-=6;
28699           H*=60;
28700           S = (M - m)/M;
28701         }
28702         p1[N] = (T)cimg::cut(H,0,360);
28703         p2[N] = (T)cimg::cut(S,0,1);
28704         p3[N] = (T)cimg::cut(M/255,0,1);
28705       }
28706       return *this;
28707     }
28708 
28709     //! Convert pixel values from RGB to HSV color spaces \newinstance.
28710     CImg<Tfloat> get_RGBtoHSV() const {
28711       return CImg<Tfloat>(*this,false).RGBtoHSV();
28712     }
28713 
28714     //! Convert pixel values from HSV to RGB color spaces.
28715     CImg<T>& HSVtoRGB() {
28716       if (_spectrum!=3)
28717         throw CImgInstanceException(_cimg_instance
28718                                     "HSVtoRGB(): Instance is not a HSV image.",
28719                                     cimg_instance);
28720 
28721       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28722       const ulongT whd = (ulongT)_width*_height*_depth;
28723       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=256))
28724       for (ulongT N = 0; N<whd; ++N) {
28725         Tfloat
28726           H = cimg::mod((Tfloat)p1[N],(Tfloat)360),
28727           S = (Tfloat)p2[N],
28728           V = (Tfloat)p3[N],
28729           R = 0, G = 0, B = 0;
28730         if (H==0 && S==0) R = G = B = V;
28731         else {
28732           H/=60;
28733           const int i = (int)std::floor(H);
28734           const Tfloat
28735             f = (i&1)?H - i:1 - H + i,
28736             m = V*(1 - S),
28737             n = V*(1 - S*f);
28738           switch (i) {
28739           case 6 :
28740           case 0 : R = V; G = n; B = m; break;
28741           case 1 : R = n; G = V; B = m; break;
28742           case 2 : R = m; G = V; B = n; break;
28743           case 3 : R = m; G = n; B = V; break;
28744           case 4 : R = n; G = m; B = V; break;
28745           case 5 : R = V; G = m; B = n; break;
28746           }
28747         }
28748         p1[N] = (T)cimg::cut(R*255,0,255);
28749         p2[N] = (T)cimg::cut(G*255,0,255);
28750         p3[N] = (T)cimg::cut(B*255,0,255);
28751       }
28752       return *this;
28753     }
28754 
28755     //! Convert pixel values from HSV to RGB color spaces \newinstance.
28756     CImg<Tuchar> get_HSVtoRGB() const {
28757       return CImg<Tuchar>(*this,false).HSVtoRGB();
28758     }
28759 
28760     //! Convert pixel values from RGB to YCbCr color spaces.
28761     CImg<T>& RGBtoYCbCr() {
28762       if (_spectrum!=3)
28763         throw CImgInstanceException(_cimg_instance
28764                                     "RGBtoYCbCr(): Instance is not a RGB image.",
28765                                     cimg_instance);
28766 
28767       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28768       const ulongT whd = (ulongT)_width*_height*_depth;
28769       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=512))
28770       for (ulongT N = 0; N<whd; ++N) {
28771         const Tfloat
28772           R = (Tfloat)p1[N],
28773           G = (Tfloat)p2[N],
28774           B = (Tfloat)p3[N],
28775           Y = (66*R + 129*G + 25*B + 128)/256 + 16,
28776           Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
28777           Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
28778         p1[N] = (T)cimg::cut(Y,0,255),
28779         p2[N] = (T)cimg::cut(Cb,0,255),
28780         p3[N] = (T)cimg::cut(Cr,0,255);
28781       }
28782       return *this;
28783     }
28784 
28785     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
28786     CImg<Tuchar> get_RGBtoYCbCr() const {
28787       return CImg<Tuchar>(*this,false).RGBtoYCbCr();
28788     }
28789 
28790     //! Convert pixel values from RGB to YCbCr color spaces.
28791     CImg<T>& YCbCrtoRGB() {
28792       if (_spectrum!=3)
28793         throw CImgInstanceException(_cimg_instance
28794                                     "YCbCrtoRGB(): Instance is not a YCbCr image.",
28795                                     cimg_instance);
28796 
28797       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28798       const ulongT whd = (ulongT)_width*_height*_depth;
28799       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=512))
28800       for (ulongT N = 0; N<whd; ++N) {
28801         const Tfloat
28802           Y = (Tfloat)p1[N] - 16,
28803           Cb = (Tfloat)p2[N] - 128,
28804           Cr = (Tfloat)p3[N] - 128,
28805           R = (298*Y + 409*Cr + 128)/256,
28806           G = (298*Y - 100*Cb - 208*Cr + 128)/256,
28807           B = (298*Y + 516*Cb + 128)/256;
28808         p1[N] = (T)cimg::cut(R,0,255),
28809         p2[N] = (T)cimg::cut(G,0,255),
28810         p3[N] = (T)cimg::cut(B,0,255);
28811       }
28812       return *this;
28813     }
28814 
28815     //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
28816     CImg<Tuchar> get_YCbCrtoRGB() const {
28817       return CImg<Tuchar>(*this,false).YCbCrtoRGB();
28818     }
28819 
28820     //! Convert pixel values from RGB to YUV color spaces.
28821     CImg<T>& RGBtoYUV() {
28822       if (_spectrum!=3)
28823         throw CImgInstanceException(_cimg_instance
28824                                     "RGBtoYUV(): Instance is not a RGB image.",
28825                                     cimg_instance);
28826 
28827       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28828       const ulongT whd = (ulongT)_width*_height*_depth;
28829       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=16384))
28830       for (ulongT N = 0; N<whd; ++N) {
28831         const Tfloat
28832           R = (Tfloat)p1[N]/255,
28833           G = (Tfloat)p2[N]/255,
28834           B = (Tfloat)p3[N]/255,
28835           Y = 0.299f*R + 0.587f*G + 0.114f*B;
28836         p1[N] = (T)Y;
28837         p2[N] = (T)(0.492f*(B - Y));
28838         p3[N] = (T)(0.877*(R - Y));
28839       }
28840       return *this;
28841     }
28842 
28843     //! Convert pixel values from RGB to YUV color spaces \newinstance.
28844     CImg<Tfloat> get_RGBtoYUV() const {
28845       return CImg<Tfloat>(*this,false).RGBtoYUV();
28846     }
28847 
28848     //! Convert pixel values from YUV to RGB color spaces.
28849     CImg<T>& YUVtoRGB() {
28850       if (_spectrum!=3)
28851         throw CImgInstanceException(_cimg_instance
28852                                     "YUVtoRGB(): Instance is not a YUV image.",
28853                                     cimg_instance);
28854 
28855       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28856       const ulongT whd = (ulongT)_width*_height*_depth;
28857       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=16384))
28858       for (ulongT N = 0; N<whd; ++N) {
28859         const Tfloat
28860           Y = (Tfloat)p1[N],
28861           U = (Tfloat)p2[N],
28862           V = (Tfloat)p3[N],
28863           R = (Y + 1.140f*V)*255,
28864           G = (Y - 0.395f*U - 0.581f*V)*255,
28865           B = (Y + 2.032f*U)*255;
28866         p1[N] = (T)cimg::cut(R,0,255),
28867         p2[N] = (T)cimg::cut(G,0,255),
28868         p3[N] = (T)cimg::cut(B,0,255);
28869       }
28870       return *this;
28871     }
28872 
28873     //! Convert pixel values from YUV to RGB color spaces \newinstance.
28874     CImg<Tuchar> get_YUVtoRGB() const {
28875       return CImg< Tuchar>(*this,false).YUVtoRGB();
28876     }
28877 
28878     //! Convert pixel values from RGB to CMY color spaces.
28879     CImg<T>& RGBtoCMY() {
28880       if (_spectrum!=3)
28881         throw CImgInstanceException(_cimg_instance
28882                                     "RGBtoCMY(): Instance is not a RGB image.",
28883                                     cimg_instance);
28884 
28885       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28886       const ulongT whd = (ulongT)_width*_height*_depth;
28887       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
28888       for (ulongT N = 0; N<whd; ++N) {
28889         const Tfloat
28890           R = (Tfloat)p1[N],
28891           G = (Tfloat)p2[N],
28892           B = (Tfloat)p3[N],
28893           C = 255 - R,
28894           M = 255 - G,
28895           Y = 255 - B;
28896         p1[N] = (T)cimg::cut(C,0,255),
28897         p2[N] = (T)cimg::cut(M,0,255),
28898         p3[N] = (T)cimg::cut(Y,0,255);
28899       }
28900       return *this;
28901     }
28902 
28903     //! Convert pixel values from RGB to CMY color spaces \newinstance.
28904     CImg<Tuchar> get_RGBtoCMY() const {
28905       return CImg<Tfloat>(*this,false).RGBtoCMY();
28906     }
28907 
28908     //! Convert pixel values from CMY to RGB color spaces.
28909     CImg<T>& CMYtoRGB() {
28910       if (_spectrum!=3)
28911         throw CImgInstanceException(_cimg_instance
28912                                     "CMYtoRGB(): Instance is not a CMY image.",
28913                                     cimg_instance);
28914 
28915       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
28916       const ulongT whd = (ulongT)_width*_height*_depth;
28917       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
28918       for (ulongT N = 0; N<whd; ++N) {
28919         const Tfloat
28920           C = (Tfloat)p1[N],
28921           M = (Tfloat)p2[N],
28922           Y = (Tfloat)p3[N],
28923           R = 255 - C,
28924           G = 255 - M,
28925           B = 255 - Y;
28926         p1[N] = (T)cimg::cut(R,0,255),
28927         p2[N] = (T)cimg::cut(G,0,255),
28928         p3[N] = (T)cimg::cut(B,0,255);
28929       }
28930       return *this;
28931     }
28932 
28933     //! Convert pixel values from CMY to RGB color spaces \newinstance.
28934     CImg<Tuchar> get_CMYtoRGB() const {
28935       return CImg<Tuchar>(*this,false).CMYtoRGB();
28936     }
28937 
28938     //! Convert pixel values from CMY to CMYK color spaces.
28939     CImg<T>& CMYtoCMYK() {
28940       return get_CMYtoCMYK().move_to(*this);
28941     }
28942 
28943     //! Convert pixel values from CMY to CMYK color spaces \newinstance.
28944     CImg<Tuchar> get_CMYtoCMYK() const {
28945       if (_spectrum!=3)
28946         throw CImgInstanceException(_cimg_instance
28947                                     "CMYtoCMYK(): Instance is not a CMY image.",
28948                                     cimg_instance);
28949 
28950       CImg<Tfloat> res(_width,_height,_depth,4);
28951       const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
28952       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);
28953       const ulongT whd = (ulongT)_width*_height*_depth;
28954       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=1024))
28955       for (ulongT N = 0; N<whd; ++N) {
28956         Tfloat
28957 	  C = (Tfloat)ps1[N],
28958 	  M = (Tfloat)ps2[N],
28959 	  Y = (Tfloat)ps3[N],
28960 	  K = cimg::min(C,M,Y);
28961 	if (K>=255) C = M = Y = 0;
28962 	else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
28963         pd1[N] = (Tfloat)cimg::cut(C,0,255),
28964         pd2[N] = (Tfloat)cimg::cut(M,0,255),
28965         pd3[N] = (Tfloat)cimg::cut(Y,0,255),
28966         pd4[N] = (Tfloat)cimg::cut(K,0,255);
28967       }
28968       return res;
28969     }
28970 
28971     //! Convert pixel values from CMYK to CMY color spaces.
28972     CImg<T>& CMYKtoCMY() {
28973       return get_CMYKtoCMY().move_to(*this);
28974     }
28975 
28976     //! Convert pixel values from CMYK to CMY color spaces \newinstance.
28977     CImg<Tfloat> get_CMYKtoCMY() const {
28978       if (_spectrum!=4)
28979         throw CImgInstanceException(_cimg_instance
28980                                     "CMYKtoCMY(): Instance is not a CMYK image.",
28981                                     cimg_instance);
28982 
28983       CImg<Tfloat> res(_width,_height,_depth,3);
28984       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);
28985       Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
28986       const ulongT whd = (ulongT)_width*_height*_depth;
28987       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=1024))
28988       for (ulongT N = 0; N<whd; ++N) {
28989         const Tfloat
28990 	  C = (Tfloat)ps1[N],
28991 	  M = (Tfloat)ps2[N],
28992 	  Y = (Tfloat)ps3[N],
28993 	  K = (Tfloat)ps4[N],
28994 	  K1 = 1 - K/255,
28995           nC = C*K1 + K,
28996           nM = M*K1 + K,
28997           nY = Y*K1 + K;
28998         pd1[N] = (Tfloat)cimg::cut(nC,0,255),
28999         pd2[N] = (Tfloat)cimg::cut(nM,0,255),
29000         pd3[N] = (Tfloat)cimg::cut(nY,0,255);
29001       }
29002       return res;
29003     }
29004 
29005     //! Convert pixel values from RGB to XYZ color spaces.
29006     /**
29007        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
29008     **/
29009     CImg<T>& RGBtoXYZ(const bool use_D65=true) {
29010       if (_spectrum!=3)
29011         throw CImgInstanceException(_cimg_instance
29012                                     "RGBtoXYZ(): Instance is not a RGB image.",
29013                                     cimg_instance);
29014 
29015       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29016       const ulongT whd = (ulongT)_width*_height*_depth;
29017       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
29018       for (ulongT N = 0; N<whd; ++N) {
29019         const Tfloat
29020           R = (Tfloat)p1[N]/255,
29021           G = (Tfloat)p2[N]/255,
29022           B = (Tfloat)p3[N]/255;
29023         if (use_D65) { // D65
29024           p1[N] = (T)(0.4124564*R + 0.3575761*G + 0.1804375*B);
29025           p2[N] = (T)(0.2126729*R + 0.7151522*G + 0.0721750*B);
29026           p3[N] = (T)(0.0193339*R + 0.1191920*G + 0.9503041*B);
29027         } else { // D50
29028           p1[N] = (T)(0.43603516*R + 0.38511658*G + 0.14305115*B);
29029           p2[N] = (T)(0.22248840*R + 0.71690369*G + 0.06060791*B);
29030           p3[N] = (T)(0.01391602*R + 0.09706116*G + 0.71392822*B);
29031         }
29032       }
29033       return *this;
29034     }
29035 
29036     //! Convert pixel values from RGB to XYZ color spaces \newinstance.
29037     CImg<Tfloat> get_RGBtoXYZ(const bool use_D65=true) const {
29038       return CImg<Tfloat>(*this,false).RGBtoXYZ(use_D65);
29039     }
29040 
29041     //! Convert pixel values from XYZ to RGB color spaces.
29042     /**
29043        \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
29044     **/
29045     CImg<T>& XYZtoRGB(const bool use_D65=true) {
29046       if (_spectrum!=3)
29047         throw CImgInstanceException(_cimg_instance
29048                                     "XYZtoRGB(): Instance is not a XYZ image.",
29049                                     cimg_instance);
29050 
29051       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29052       const ulongT whd = (ulongT)_width*_height*_depth;
29053       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=2048))
29054       for (ulongT N = 0; N<whd; ++N) {
29055         const Tfloat
29056           X = (Tfloat)p1[N]*255,
29057           Y = (Tfloat)p2[N]*255,
29058           Z = (Tfloat)p3[N]*255;
29059         if (use_D65) {
29060           p1[N] = (T)cimg::cut(3.2404542*X - 1.5371385*Y - 0.4985314*Z,0,255);
29061           p2[N] = (T)cimg::cut(-0.9692660*X + 1.8760108*Y + 0.0415560*Z,0,255);
29062           p3[N] = (T)cimg::cut(0.0556434*X - 0.2040259*Y + 1.0572252*Z,0,255);
29063         } else {
29064           p1[N] = (T)cimg::cut(3.134274799724*X  - 1.617275708956*Y - 0.490724283042*Z,0,255);
29065           p2[N] = (T)cimg::cut(-0.978795575994*X + 1.916161689117*Y + 0.033453331711*Z,0,255);
29066           p3[N] = (T)cimg::cut(0.071976988401*X - 0.228984974402*Y + 1.405718224383*Z,0,255);
29067         }
29068       }
29069       return *this;
29070     }
29071 
29072     //! Convert pixel values from XYZ to RGB color spaces \newinstance.
29073     CImg<Tuchar> get_XYZtoRGB(const bool use_D65=true) const {
29074       return CImg<Tuchar>(*this,false).XYZtoRGB(use_D65);
29075     }
29076 
29077     //! Convert pixel values from XYZ to Lab color spaces.
29078     CImg<T>& XYZtoLab(const bool use_D65=true) {
29079 #define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116)
29080 
29081       if (_spectrum!=3)
29082         throw CImgInstanceException(_cimg_instance
29083                                     "XYZtoLab(): Instance is not a XYZ image.",
29084                                     cimg_instance);
29085       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
29086       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29087       const ulongT whd = (ulongT)_width*_height*_depth;
29088       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=128))
29089       for (ulongT N = 0; N<whd; ++N) {
29090         const Tfloat
29091           X = (Tfloat)(p1[N]/white[0]),
29092           Y = (Tfloat)(p2[N]/white[1]),
29093           Z = (Tfloat)(p3[N]/white[2]),
29094           fX = (Tfloat)_cimg_Labf(X),
29095           fY = (Tfloat)_cimg_Labf(Y),
29096           fZ = (Tfloat)_cimg_Labf(Z);
29097         p1[N] = (T)cimg::cut(116*fY - 16,0,100);
29098         p2[N] = (T)(500*(fX - fY));
29099         p3[N] = (T)(200*(fY - fZ));
29100       }
29101       return *this;
29102     }
29103 
29104     //! Convert pixel values from XYZ to Lab color spaces \newinstance.
29105     CImg<Tfloat> get_XYZtoLab(const bool use_D65=true) const {
29106       return CImg<Tfloat>(*this,false).XYZtoLab(use_D65);
29107     }
29108 
29109     //! Convert pixel values from Lab to XYZ color spaces.
29110     CImg<T>& LabtoXYZ(const bool use_D65=true) {
29111       if (_spectrum!=3)
29112         throw CImgInstanceException(_cimg_instance
29113                                     "LabtoXYZ(): Instance is not a Lab image.",
29114                                     cimg_instance);
29115       const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
29116       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29117       const ulongT whd = (ulongT)_width*_height*_depth;
29118       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=128))
29119       for (ulongT N = 0; N<whd; ++N) {
29120         const Tfloat
29121           L = (Tfloat)p1[N],
29122           a = (Tfloat)p2[N],
29123           b = (Tfloat)p3[N],
29124           cY = (L + 16)/116,
29125           cZ = cY - b/200,
29126           cX = a/500 + cY,
29127           X = (Tfloat)(24389*cX>216?cX*cX*cX:(116*cX - 16)*27/24389),
29128           Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389),
29129           Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389);
29130         p1[N] = (T)(X*white[0]);
29131         p2[N] = (T)(Y*white[1]);
29132         p3[N] = (T)(Z*white[2]);
29133       }
29134       return *this;
29135     }
29136 
29137     //! Convert pixel values from Lab to XYZ color spaces \newinstance.
29138     CImg<Tfloat> get_LabtoXYZ(const bool use_D65=true) const {
29139       return CImg<Tfloat>(*this,false).LabtoXYZ(use_D65);
29140     }
29141 
29142     //! Convert pixel values from XYZ to xyY color spaces.
29143     CImg<T>& XYZtoxyY() {
29144       if (_spectrum!=3)
29145         throw CImgInstanceException(_cimg_instance
29146                                     "XYZtoxyY(): Instance is not a XYZ image.",
29147                                     cimg_instance);
29148 
29149       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29150       const ulongT whd = (ulongT)_width*_height*_depth;
29151       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=4096))
29152       for (ulongT N = 0; N<whd; ++N) {
29153         const Tfloat
29154           X = (Tfloat)p1[N],
29155           Y = (Tfloat)p2[N],
29156           Z = (Tfloat)p3[N],
29157           sum = X + Y + Z,
29158           nsum = sum>0?sum:1;
29159         p1[N] = (T)(X/nsum);
29160         p2[N] = (T)(Y/nsum);
29161         p3[N] = (T)Y;
29162       }
29163       return *this;
29164     }
29165 
29166     //! Convert pixel values from XYZ to xyY color spaces \newinstance.
29167     CImg<Tfloat> get_XYZtoxyY() const {
29168       return CImg<Tfloat>(*this,false).XYZtoxyY();
29169     }
29170 
29171     //! Convert pixel values from xyY pixels to XYZ color spaces.
29172     CImg<T>& xyYtoXYZ() {
29173       if (_spectrum!=3)
29174         throw CImgInstanceException(_cimg_instance
29175                                     "xyYtoXYZ(): Instance is not a xyY image.",
29176                                     cimg_instance);
29177 
29178       T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
29179       const ulongT whd = (ulongT)_width*_height*_depth;
29180       cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=4096))
29181       for (ulongT N = 0; N<whd; ++N) {
29182         const Tfloat
29183          px = (Tfloat)p1[N],
29184          py = (Tfloat)p2[N],
29185          Y = (Tfloat)p3[N],
29186          ny = py>0?py:1;
29187         p1[N] = (T)(px*Y/ny);
29188         p2[N] = (T)Y;
29189         p3[N] = (T)((1 - px - py)*Y/ny);
29190       }
29191       return *this;
29192     }
29193 
29194     //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance.
29195     CImg<Tfloat> get_xyYtoXYZ() const {
29196       return CImg<Tfloat>(*this,false).xyYtoXYZ();
29197     }
29198 
29199     //! Convert pixel values from RGB to Lab color spaces.
29200     CImg<T>& RGBtoLab(const bool use_D65=true) {
29201       return RGBtoXYZ(use_D65).XYZtoLab(use_D65);
29202     }
29203 
29204     //! Convert pixel values from RGB to Lab color spaces \newinstance.
29205     CImg<Tfloat> get_RGBtoLab(const bool use_D65=true) const {
29206       return CImg<Tfloat>(*this,false).RGBtoLab(use_D65);
29207     }
29208 
29209     //! Convert pixel values from Lab to RGB color spaces.
29210     CImg<T>& LabtoRGB(const bool use_D65=true) {
29211       return LabtoXYZ().XYZtoRGB(use_D65);
29212     }
29213 
29214     //! Convert pixel values from Lab to RGB color spaces \newinstance.
29215     CImg<Tuchar> get_LabtoRGB(const bool use_D65=true) const {
29216       return CImg<Tuchar>(*this,false).LabtoRGB(use_D65);
29217     }
29218 
29219     //! Convert pixel values from RGB to xyY color spaces.
29220     CImg<T>& RGBtoxyY(const bool use_D65=true) {
29221       return RGBtoXYZ(use_D65).XYZtoxyY();
29222     }
29223 
29224     //! Convert pixel values from RGB to xyY color spaces \newinstance.
29225     CImg<Tfloat> get_RGBtoxyY(const bool use_D65=true) const {
29226       return CImg<Tfloat>(*this,false).RGBtoxyY(use_D65);
29227     }
29228 
29229     //! Convert pixel values from xyY to RGB color spaces.
29230     CImg<T>& xyYtoRGB(const bool use_D65=true) {
29231       return xyYtoXYZ().XYZtoRGB(use_D65);
29232     }
29233 
29234     //! Convert pixel values from xyY to RGB color spaces \newinstance.
29235     CImg<Tuchar> get_xyYtoRGB(const bool use_D65=true) const {
29236       return CImg<Tuchar>(*this,false).xyYtoRGB(use_D65);
29237     }
29238 
29239     //! Convert pixel values from RGB to CMYK color spaces.
29240     CImg<T>& RGBtoCMYK() {
29241       return RGBtoCMY().CMYtoCMYK();
29242     }
29243 
29244     //! Convert pixel values from RGB to CMYK color spaces \newinstance.
29245     CImg<Tfloat> get_RGBtoCMYK() const {
29246       return CImg<Tfloat>(*this,false).RGBtoCMYK();
29247     }
29248 
29249     //! Convert pixel values from CMYK to RGB color spaces.
29250     CImg<T>& CMYKtoRGB() {
29251       return CMYKtoCMY().CMYtoRGB();
29252     }
29253 
29254     //! Convert pixel values from CMYK to RGB color spaces \newinstance.
29255     CImg<Tuchar> get_CMYKtoRGB() const {
29256       return CImg<Tuchar>(*this,false).CMYKtoRGB();
29257     }
29258 
29259     //@}
29260     //------------------------------------------
29261     //
29262     //! \name Geometric / Spatial Manipulation
29263     //@{
29264     //------------------------------------------
29265 
29266     static float _cimg_lanczos(const float x) {
29267       if (x<=-2 || x>=2) return 0;
29268       const float a = (float)cimg::PI*x, b = 0.5f*a;
29269       return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
29270     }
29271 
29272     //! Resize image to new dimensions.
29273     /**
29274        \param size_x Number of columns (new size along the X-axis).
29275        \param size_y Number of rows (new size along the Y-axis).
29276        \param size_z Number of slices (new size along the Z-axis).
29277        \param size_c Number of vector-channels (new size along the C-axis).
29278        \param interpolation_type Method of interpolation:
29279        - -1 = no interpolation: raw memory resizing.
29280        - 0 = no interpolation: additional space is filled according to \p boundary_conditions.
29281        - 1 = nearest-neighbor interpolation.
29282        - 2 = moving average interpolation.
29283        - 3 = linear interpolation.
29284        - 4 = grid interpolation.
29285        - 5 = cubic interpolation.
29286        - 6 = lanczos interpolation.
29287        \param boundary_conditions Type of boundary conditions used if necessary.
29288        \param centering_x Set centering type (only if \p interpolation_type=0).
29289        \param centering_y Set centering type (only if \p interpolation_type=0).
29290        \param centering_z Set centering type (only if \p interpolation_type=0).
29291        \param centering_c Set centering type (only if \p interpolation_type=0).
29292        \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
29293     **/
29294     CImg<T>& resize(const int size_x, const int size_y=-100,
29295                     const int size_z=-100, const int size_c=-100,
29296                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
29297                     const float centering_x = 0, const float centering_y = 0,
29298                     const float centering_z = 0, const float centering_c = 0) {
29299       if (!size_x || !size_y || !size_z || !size_c) return assign();
29300       const unsigned int
29301         _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
29302         _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
29303         _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
29304         _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
29305         sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
29306       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
29307       if (is_empty()) return assign(sx,sy,sz,sc,(T)0);
29308       if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
29309 	_width = sx; _height = sy; _depth = sz; _spectrum = sc;
29310 	return *this;
29311       }
29312       return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,
29313                         centering_x,centering_y,centering_z,centering_c).move_to(*this);
29314     }
29315 
29316     //! Resize image to new dimensions \newinstance.
29317     CImg<T> get_resize(const int size_x, const int size_y = -100,
29318                        const int size_z = -100, const int size_c = -100,
29319                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
29320                        const float centering_x = 0, const float centering_y = 0,
29321                        const float centering_z = 0, const float centering_c = 0) const {
29322       if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
29323           centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
29324         throw CImgArgumentException(_cimg_instance
29325                                     "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
29326                                     cimg_instance,
29327                                     centering_x,centering_y,centering_z,centering_c);
29328 
29329       if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
29330       const unsigned int
29331         sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)),
29332         sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)),
29333         sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)),
29334         sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100));
29335       if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
29336       if (is_empty()) return CImg<T>(sx,sy,sz,sc,(T)0);
29337       CImg<T> res;
29338       switch (interpolation_type) {
29339 
29340         // Raw resizing.
29341         //
29342       case -1 :
29343         std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc));
29344         break;
29345 
29346         // No interpolation.
29347         //
29348       case 0 : {
29349         const int
29350           xc = (int)(centering_x*((int)sx - width())),
29351           yc = (int)(centering_y*((int)sy - height())),
29352           zc = (int)(centering_z*((int)sz - depth())),
29353           cc = (int)(centering_c*((int)sc - spectrum()));
29354 
29355         switch (boundary_conditions) {
29356         case 3 : { // Mirror
29357           res.assign(sx,sy,sz,sc);
29358           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
29359           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=65536))
29360           cimg_forXYZC(res,x,y,z,c) {
29361             const int
29362               mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2),
29363               mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2);
29364             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
29365                                    my<height()?my:h2 - my - 1,
29366                                    mz<depth()?mz:d2 - mz - 1,
29367                                    mc<spectrum()?mc:s2 - mc - 1);
29368           }
29369         } break;
29370         case 2 : { // Periodic
29371           res.assign(sx,sy,sz,sc);
29372           const int
29373             x0 = ((int)xc%width()) - width(),
29374             y0 = ((int)yc%height()) - height(),
29375             z0 = ((int)zc%depth()) - depth(),
29376             c0 = ((int)cc%spectrum()) - spectrum(),
29377             dx = width(), dy = height(), dz = depth(), dc = spectrum();
29378           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=65536))
29379           for (int c = c0; c<(int)sc; c+=dc)
29380             for (int z = z0; z<(int)sz; z+=dz)
29381               for (int y = y0; y<(int)sy; y+=dy)
29382                 for (int x = x0; x<(int)sx; x+=dx)
29383                   res.draw_image(x,y,z,c,*this);
29384         } break;
29385         case 1 : { // Neumann
29386           res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
29387           CImg<T> sprite;
29388           if (xc>0) {  // X-backward
29389             res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29390             for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
29391           }
29392           if (xc + width()<(int)sx) { // X-forward
29393             res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1,
29394                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29395             for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
29396           }
29397           if (yc>0) {  // Y-backward
29398             res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29399             for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
29400           }
29401           if (yc + height()<(int)sy) { // Y-forward
29402             res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1,
29403                          zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29404             for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
29405           }
29406           if (zc>0) {  // Z-backward
29407             res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite);
29408             for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
29409           }
29410           if (zc + depth()<(int)sz) { // Z-forward
29411             res.get_crop(0,0,zc  +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
29412             for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
29413           }
29414           if (cc>0) {  // C-backward
29415             res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite);
29416             for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
29417           }
29418           if (cc + spectrum()<(int)sc) { // C-forward
29419             res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite);
29420             for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
29421           }
29422         } break;
29423         default : // Dirichlet
29424           res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this);
29425         }
29426         break;
29427       } break;
29428 
29429         // Nearest neighbor interpolation.
29430         //
29431       case 1 : {
29432         res.assign(sx,sy,sz,sc);
29433         CImg<ulongT> off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1);
29434         const ulongT
29435           wh = (ulongT)_width*_height,
29436           whd = (ulongT)_width*_height*_depth,
29437           sxy = (ulongT)sx*sy,
29438           sxyz = (ulongT)sx*sy*sz;
29439         if (sx==_width) off_x.fill(1);
29440         else {
29441           ulongT *poff_x = off_x._data, curr = 0;
29442           cimg_forX(res,x) {
29443             const ulongT old = curr;
29444             curr = (ulongT)((x + 1.0)*_width/sx);
29445             *(poff_x++) = curr - old;
29446           }
29447         }
29448         if (sy==_height) off_y.fill(_width);
29449         else {
29450           ulongT *poff_y = off_y._data, curr = 0;
29451           cimg_forY(res,y) {
29452             const ulongT old = curr;
29453             curr = (ulongT)((y + 1.0)*_height/sy);
29454             *(poff_y++) = _width*(curr - old);
29455           }
29456           *poff_y = 0;
29457         }
29458         if (sz==_depth) off_z.fill(wh);
29459         else {
29460           ulongT *poff_z = off_z._data, curr = 0;
29461           cimg_forZ(res,z) {
29462             const ulongT old = curr;
29463             curr = (ulongT)((z + 1.0)*_depth/sz);
29464             *(poff_z++) = wh*(curr - old);
29465           }
29466           *poff_z = 0;
29467         }
29468         if (sc==_spectrum) off_c.fill(whd);
29469         else {
29470           ulongT *poff_c = off_c._data, curr = 0;
29471           cimg_forC(res,c) {
29472             const ulongT old = curr;
29473             curr = (ulongT)((c + 1.0)*_spectrum/sc);
29474             *(poff_c++) = whd*(curr - old);
29475           }
29476           *poff_c = 0;
29477         }
29478 
29479         T *ptrd = res._data;
29480         const T* ptrc = _data;
29481         const ulongT *poff_c = off_c._data;
29482         for (unsigned int c = 0; c<sc; ) {
29483           const T *ptrz = ptrc;
29484           const ulongT *poff_z = off_z._data;
29485           for (unsigned int z = 0; z<sz; ) {
29486             const T *ptry = ptrz;
29487             const ulongT *poff_y = off_y._data;
29488             for (unsigned int y = 0; y<sy; ) {
29489               const T *ptrx = ptry;
29490               const ulongT *poff_x = off_x._data;
29491               cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
29492               ++y;
29493               ulongT dy = *(poff_y++);
29494               for ( ; !dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
29495               ptry+=dy;
29496             }
29497             ++z;
29498             ulongT dz = *(poff_z++);
29499             for ( ; !dz && z<dz; std::memcpy(ptrd,ptrd-sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
29500             ptrz+=dz;
29501           }
29502           ++c;
29503           ulongT dc = *(poff_c++);
29504           for ( ; !dc && c<dc; std::memcpy(ptrd,ptrd-sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
29505           ptrc+=dc;
29506         }
29507       } break;
29508 
29509         // Moving average.
29510         //
29511       case 2 : {
29512         bool instance_first = true;
29513         if (sx!=_width) {
29514           CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
29515           for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
29516             const unsigned int d = std::min(b,c);
29517             a-=d; b-=d; c-=d;
29518             cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
29519             if (!b) {
29520               cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width;
29521               ++t;
29522               b = _width;
29523             }
29524             if (!c) { ++s; c = sx; }
29525           }
29526           tmp.move_to(res);
29527           instance_first = false;
29528         }
29529         if (sy!=_height) {
29530           CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
29531           for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
29532             const unsigned int d = std::min(b,c);
29533             a-=d; b-=d; c-=d;
29534             if (instance_first)
29535               cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
29536             else
29537               cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
29538             if (!b) {
29539               cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height;
29540               ++t;
29541               b = _height;
29542             }
29543             if (!c) { ++s; c = sy; }
29544           }
29545           tmp.move_to(res);
29546           instance_first = false;
29547         }
29548         if (sz!=_depth) {
29549           CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
29550           for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
29551             const unsigned int d = std::min(b,c);
29552             a-=d; b-=d; c-=d;
29553             if (instance_first)
29554               cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
29555             else
29556               cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
29557             if (!b) {
29558               cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth;
29559               ++t;
29560               b = _depth;
29561             }
29562             if (!c) { ++s; c = sz; }
29563           }
29564           tmp.move_to(res);
29565           instance_first = false;
29566         }
29567         if (sc!=_spectrum) {
29568           CImg<Tfloat> tmp(sx,sy,sz,sc,0);
29569           for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
29570             const unsigned int d = std::min(b,c);
29571             a-=d; b-=d; c-=d;
29572             if (instance_first)
29573               cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
29574             else
29575               cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
29576             if (!b) {
29577               cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum;
29578               ++t;
29579               b = _spectrum;
29580             }
29581             if (!c) { ++s; c = sc; }
29582           }
29583           tmp.move_to(res);
29584           instance_first = false;
29585         }
29586       } break;
29587 
29588         // Linear interpolation.
29589         //
29590       case 3 : {
29591         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
29592         CImg<doubleT> foff(off._width);
29593         CImg<T> resx, resy, resz, resc;
29594         double curr, old;
29595 
29596         if (sx!=_width) {
29597           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
29598           else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
29599           else {
29600             const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0):
29601               (double)_width/sx;
29602             resx.assign(sx,_height,_depth,_spectrum);
29603             curr = old = 0;
29604             unsigned int *poff = off._data;
29605             double *pfoff = foff._data;
29606             cimg_forX(resx,x) {
29607               *(pfoff++) = curr - (unsigned int)curr;
29608               old = curr;
29609               curr = std::min(width() - 1.0,curr + fx);
29610               *(poff++) = (unsigned int)curr - (unsigned int)old;
29611             }
29612             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resx.size()>=65536))
29613               cimg_forYZC(resx,y,z,c) {
29614               const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1;
29615               T *ptrd = resx.data(0,y,z,c);
29616               const unsigned int *poff = off._data;
29617               const double *pfoff = foff._data;
29618               cimg_forX(resx,x) {
29619                 const double alpha = *(pfoff++);
29620                 const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + 1):val1;
29621                 *(ptrd++) = (T)((1 - alpha)*val1 + alpha*val2);
29622                 ptrs+=*(poff++);
29623               }
29624             }
29625           }
29626         } else resx.assign(*this,true);
29627 
29628         if (sy!=_height) {
29629           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
29630           else {
29631             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
29632             else {
29633               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.0)/(sy - 1):0):
29634                 (double)_height/sy;
29635               resy.assign(sx,sy,_depth,_spectrum);
29636               curr = old = 0;
29637               unsigned int *poff = off._data;
29638               double *pfoff = foff._data;
29639               cimg_forY(resy,y) {
29640                 *(pfoff++) = curr - (unsigned int)curr;
29641                 old = curr;
29642                 curr = std::min(height() - 1.0,curr + fy);
29643                 *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
29644               }
29645               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resy.size()>=65536))
29646               cimg_forXZC(resy,x,z,c) {
29647                 const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx;
29648                 T *ptrd = resy.data(x,0,z,c);
29649                 const unsigned int *poff = off._data;
29650                 const double *pfoff = foff._data;
29651                 cimg_forY(resy,y) {
29652                   const double alpha = *(pfoff++);
29653                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sx):val1;
29654                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
29655                   ptrd+=sx;
29656                   ptrs+=*(poff++);
29657                 }
29658               }
29659             }
29660           }
29661           resx.assign();
29662         } else resy.assign(resx,true);
29663 
29664         if (sz!=_depth) {
29665           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
29666           else {
29667             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
29668             else {
29669               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.0)/(sz - 1):0):
29670                 (double)_depth/sz;
29671               const unsigned int sxy = sx*sy;
29672               resz.assign(sx,sy,sz,_spectrum);
29673               curr = old = 0;
29674               unsigned int *poff = off._data;
29675               double *pfoff = foff._data;
29676               cimg_forZ(resz,z) {
29677                 *(pfoff++) = curr - (unsigned int)curr;
29678                 old = curr;
29679                 curr = std::min(depth() - 1.0,curr + fz);
29680                 *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
29681               }
29682               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resz.size()>=65536))
29683               cimg_forXYC(resz,x,y,c) {
29684                 const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy;
29685                 T *ptrd = resz.data(x,y,0,c);
29686                 const unsigned int *poff = off._data;
29687                 const double *pfoff = foff._data;
29688                 cimg_forZ(resz,z) {
29689                   const double alpha = *(pfoff++);
29690                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxy):val1;
29691                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
29692                   ptrd+=sxy;
29693                   ptrs+=*(poff++);
29694                 }
29695               }
29696             }
29697           }
29698           resy.assign();
29699         } else resz.assign(resy,true);
29700 
29701         if (sc!=_spectrum) {
29702           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
29703           else {
29704             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
29705             else {
29706               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.0)/(sc - 1):0):
29707                 (double)_spectrum/sc;
29708               const unsigned int sxyz = sx*sy*sz;
29709               resc.assign(sx,sy,sz,sc);
29710               curr = old = 0;
29711               unsigned int *poff = off._data;
29712               double *pfoff = foff._data;
29713               cimg_forC(resc,c) {
29714                 *(pfoff++) = curr - (unsigned int)curr;
29715                 old = curr;
29716                 curr = std::min(spectrum() - 1.0,curr + fc);
29717                 *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
29718               }
29719               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resc.size()>=65536))
29720               cimg_forXYZ(resc,x,y,z) {
29721                 const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz;
29722                 T *ptrd = resc.data(x,y,z,0);
29723                 const unsigned int *poff = off._data;
29724                 const double *pfoff = foff._data;
29725                 cimg_forC(resc,c) {
29726                   const double alpha = *(pfoff++);
29727                   const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxyz):val1;
29728                   *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
29729                   ptrd+=sxyz;
29730                   ptrs+=*(poff++);
29731                 }
29732               }
29733             }
29734           }
29735           resz.assign();
29736         } else resc.assign(resz,true);
29737         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
29738       } break;
29739 
29740         // Grid interpolation.
29741         //
29742       case 4 : {
29743         CImg<T> resx, resy, resz, resc;
29744         if (sx!=_width) {
29745           if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
29746           else {
29747             resx.assign(sx,_height,_depth,_spectrum,(T)0);
29748             const int dx = (int)(2*sx), dy = 2*width();
29749             int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0;
29750             cimg_forX(resx,x) if ((err-=dy)<=0) {
29751               cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
29752               ++xs;
29753               err+=dx;
29754             }
29755           }
29756         } else resx.assign(*this,true);
29757 
29758         if (sy!=_height) {
29759           if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
29760           else {
29761             resy.assign(sx,sy,_depth,_spectrum,(T)0);
29762             const int dx = (int)(2*sy), dy = 2*height();
29763             int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0;
29764             cimg_forY(resy,y) if ((err-=dy)<=0) {
29765               cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
29766               ++ys;
29767               err+=dx;
29768             }
29769           }
29770           resx.assign();
29771         } else resy.assign(resx,true);
29772 
29773         if (sz!=_depth) {
29774           if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
29775           else {
29776             resz.assign(sx,sy,sz,_spectrum,(T)0);
29777             const int dx = (int)(2*sz), dy = 2*depth();
29778             int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0;
29779             cimg_forZ(resz,z) if ((err-=dy)<=0) {
29780               cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
29781               ++zs;
29782               err+=dx;
29783             }
29784           }
29785           resy.assign();
29786         } else resz.assign(resy,true);
29787 
29788         if (sc!=_spectrum) {
29789           if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
29790           else {
29791             resc.assign(sx,sy,sz,sc,(T)0);
29792             const int dx = (int)(2*sc), dy = 2*spectrum();
29793             int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0;
29794             cimg_forC(resc,c) if ((err-=dy)<=0) {
29795               cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
29796               ++cs;
29797               err+=dx;
29798             }
29799           }
29800           resz.assign();
29801         } else resc.assign(resz,true);
29802 
29803         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
29804       } break;
29805 
29806         // Cubic interpolation.
29807         //
29808       case 5 : {
29809         const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
29810         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
29811         CImg<doubleT> foff(off._width);
29812         CImg<T> resx, resy, resz, resc;
29813         double curr, old;
29814 
29815         if (sx!=_width) {
29816           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
29817           else {
29818             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
29819             else {
29820               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0):
29821                 (double)_width/sx;
29822               resx.assign(sx,_height,_depth,_spectrum);
29823               curr = old = 0;
29824               unsigned int *poff = off._data;
29825               double *pfoff = foff._data;
29826               cimg_forX(resx,x) {
29827                 *(pfoff++) = curr - (unsigned int)curr;
29828                 old = curr;
29829                 curr = std::min(width() - 1.0,curr + fx);
29830                 *(poff++) = (unsigned int)curr - (unsigned int)old;
29831               }
29832               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resx.size()>=65536))
29833               cimg_forYZC(resx,y,z,c) {
29834                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2);
29835                 T *ptrd = resx.data(0,y,z,c);
29836                 const unsigned int *poff = off._data;
29837                 const double *pfoff = foff._data;
29838                 cimg_forX(resx,x) {
29839                   const double
29840                     t = *(pfoff++),
29841                     val1 = (double)*ptrs,
29842                     val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1,
29843                     val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1,
29844                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2):val2,
29845                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
29846                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
29847                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
29848                   ptrs+=*(poff++);
29849                 }
29850               }
29851             }
29852           }
29853         } else resx.assign(*this,true);
29854 
29855         if (sy!=_height) {
29856           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
29857           else {
29858             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
29859             else {
29860               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.0)/(sy - 1):0):
29861                 (double)_height/sy;
29862               resy.assign(sx,sy,_depth,_spectrum);
29863               curr = old = 0;
29864               unsigned int *poff = off._data;
29865               double *pfoff = foff._data;
29866               cimg_forY(resy,y) {
29867                 *(pfoff++) = curr - (unsigned int)curr;
29868                 old = curr;
29869                 curr = std::min(height() - 1.0,curr + fy);
29870                 *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
29871               }
29872               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resy.size()>=65536))
29873               cimg_forXZC(resy,x,z,c) {
29874                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx;
29875                 T *ptrd = resy.data(x,0,z,c);
29876                 const unsigned int *poff = off._data;
29877                 const double *pfoff = foff._data;
29878                 cimg_forY(resy,y) {
29879                   const double
29880                     t = *(pfoff++),
29881                     val1 = (double)*ptrs,
29882                     val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1,
29883                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1,
29884                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val2,
29885                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
29886                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
29887                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
29888                   ptrd+=sx;
29889                   ptrs+=*(poff++);
29890                 }
29891               }
29892             }
29893           }
29894           resx.assign();
29895         } else resy.assign(resx,true);
29896 
29897         if (sz!=_depth) {
29898           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
29899           else {
29900             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
29901             else {
29902               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.0)/(sz - 1):0):
29903                 (double)_depth/sz;
29904               const unsigned int sxy = sx*sy;
29905               resz.assign(sx,sy,sz,_spectrum);
29906               curr = old = 0;
29907               unsigned int *poff = off._data;
29908               double *pfoff = foff._data;
29909               cimg_forZ(resz,z) {
29910                 *(pfoff++) = curr - (unsigned int)curr;
29911                 old = curr;
29912                 curr = std::min(depth() - 1.0,curr + fz);
29913                 *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
29914               }
29915               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resz.size()>=65536))
29916               cimg_forXYC(resz,x,y,c) {
29917                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy;
29918                 T *ptrd = resz.data(x,y,0,c);
29919                 const unsigned int *poff = off._data;
29920                 const double *pfoff = foff._data;
29921                 cimg_forZ(resz,z) {
29922                   const double
29923                     t = *(pfoff++),
29924                     val1 = (double)*ptrs,
29925                     val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1,
29926                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1,
29927                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val2,
29928                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
29929                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
29930                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
29931                   ptrd+=sxy;
29932                   ptrs+=*(poff++);
29933                 }
29934               }
29935             }
29936           }
29937           resy.assign();
29938         } else resz.assign(resy,true);
29939 
29940         if (sc!=_spectrum) {
29941           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
29942           else {
29943             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
29944             else {
29945               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.0)/(sc - 1):0):
29946                 (double)_spectrum/sc;
29947               const unsigned int sxyz = sx*sy*sz;
29948               resc.assign(sx,sy,sz,sc);
29949               curr = old = 0;
29950               unsigned int *poff = off._data;
29951               double *pfoff = foff._data;
29952               cimg_forC(resc,c) {
29953                 *(pfoff++) = curr - (unsigned int)curr;
29954                 old = curr;
29955                 curr = std::min(spectrum() - 1.0,curr + fc);
29956                 *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
29957               }
29958               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resc.size()>=65536))
29959               cimg_forXYZ(resc,x,y,z) {
29960                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
29961                 T *ptrd = resc.data(x,y,z,0);
29962                 const unsigned int *poff = off._data;
29963                 const double *pfoff = foff._data;
29964                 cimg_forC(resc,c) {
29965                   const double
29966                     t = *(pfoff++),
29967                     val1 = (double)*ptrs,
29968                     val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1,
29969                     val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1,
29970                     val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val2,
29971                     val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
29972                                        t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
29973                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
29974                   ptrd+=sxyz;
29975                   ptrs+=*(poff++);
29976                 }
29977               }
29978             }
29979           }
29980           resz.assign();
29981         } else resc.assign(resz,true);
29982 
29983         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
29984       } break;
29985 
29986         // Lanczos interpolation.
29987         //
29988       case 6 : {
29989         const double vmin = (double)cimg::type<T>::min(), vmax = (double)cimg::type<T>::max();
29990         CImg<uintT> off(cimg::max(sx,sy,sz,sc));
29991         CImg<doubleT> foff(off._width);
29992         CImg<T> resx, resy, resz, resc;
29993         double curr, old;
29994 
29995         if (sx!=_width) {
29996           if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
29997           else {
29998             if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
29999             else {
30000               const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.0)/(sx - 1):0):
30001                 (double)_width/sx;
30002               resx.assign(sx,_height,_depth,_spectrum);
30003               curr = old = 0;
30004               unsigned int *poff = off._data;
30005               double *pfoff = foff._data;
30006               cimg_forX(resx,x) {
30007                 *(pfoff++) = curr - (unsigned int)curr;
30008                 old = curr;
30009                 curr = std::min(width() - 1.0,curr + fx);
30010                 *(poff++) = (unsigned int)curr - (unsigned int)old;
30011               }
30012               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resx.size()>=65536))
30013               cimg_forYZC(resx,y,z,c) {
30014                 const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1,
30015                   *const ptrsmax = ptrs0 + (_width - 2);
30016                 T *ptrd = resx.data(0,y,z,c);
30017                 const unsigned int *poff = off._data;
30018                 const double *pfoff = foff._data;
30019                 cimg_forX(resx,x) {
30020                   const double
30021                     t = *(pfoff++),
30022                     w0 = _cimg_lanczos(t + 2),
30023                     w1 = _cimg_lanczos(t + 1),
30024                     w2 = _cimg_lanczos(t),
30025                     w3 = _cimg_lanczos(t - 1),
30026                     w4 = _cimg_lanczos(t - 2),
30027                     val2 = (double)*ptrs,
30028                     val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2,
30029                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1,
30030                     val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2,
30031                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2):val3,
30032                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
30033                   *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
30034                   ptrs+=*(poff++);
30035                 }
30036               }
30037             }
30038           }
30039         } else resx.assign(*this,true);
30040 
30041         if (sy!=_height) {
30042           if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
30043           else {
30044             if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
30045             else {
30046               const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.0)/(sy - 1):0):
30047                 (double)_height/sy;
30048               resy.assign(sx,sy,_depth,_spectrum);
30049               curr = old = 0;
30050               unsigned int *poff = off._data;
30051               double *pfoff = foff._data;
30052               cimg_forY(resy,y) {
30053                 *(pfoff++) = curr - (unsigned int)curr;
30054                 old = curr;
30055                 curr = std::min(height() - 1.0,curr + fy);
30056                 *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
30057               }
30058               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resy.size()>=65536))
30059               cimg_forXZC(resy,x,z,c) {
30060                 const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx,
30061                   *const ptrsmax = ptrs0 + (_height - 2)*sx;
30062                 T *ptrd = resy.data(x,0,z,c);
30063                 const unsigned int *poff = off._data;
30064                 const double *pfoff = foff._data;
30065                 cimg_forY(resy,y) {
30066                   const double
30067                     t = *(pfoff++),
30068                     w0 = _cimg_lanczos(t + 2),
30069                     w1 = _cimg_lanczos(t + 1),
30070                     w2 = _cimg_lanczos(t),
30071                     w3 = _cimg_lanczos(t - 1),
30072                     w4 = _cimg_lanczos(t - 2),
30073                     val2 = (double)*ptrs,
30074                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2,
30075                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1,
30076                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2,
30077                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val3,
30078                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
30079                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
30080                   ptrd+=sx;
30081                   ptrs+=*(poff++);
30082                 }
30083               }
30084             }
30085           }
30086           resx.assign();
30087         } else resy.assign(resx,true);
30088 
30089         if (sz!=_depth) {
30090           if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
30091           else {
30092             if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
30093             else {
30094               const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.0)/(sz - 1):0):
30095                 (double)_depth/sz;
30096               const unsigned int sxy = sx*sy;
30097               resz.assign(sx,sy,sz,_spectrum);
30098               curr = old = 0;
30099               unsigned int *poff = off._data;
30100               double *pfoff = foff._data;
30101               cimg_forZ(resz,z) {
30102                 *(pfoff++) = curr - (unsigned int)curr;
30103                 old = curr;
30104                 curr = std::min(depth() - 1.0,curr + fz);
30105                 *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
30106               }
30107               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resz.size()>=65536))
30108               cimg_forXYC(resz,x,y,c) {
30109                 const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy,
30110                   *const ptrsmax = ptrs0 + (_depth - 2)*sxy;
30111                 T *ptrd = resz.data(x,y,0,c);
30112                 const unsigned int *poff = off._data;
30113                 const double *pfoff = foff._data;
30114                 cimg_forZ(resz,z) {
30115                   const double
30116                     t = *(pfoff++),
30117                     w0 = _cimg_lanczos(t + 2),
30118                     w1 = _cimg_lanczos(t + 1),
30119                     w2 = _cimg_lanczos(t),
30120                     w3 = _cimg_lanczos(t - 1),
30121                     w4 = _cimg_lanczos(t - 2),
30122                     val2 = (double)*ptrs,
30123                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2,
30124                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1,
30125                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2,
30126                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val3,
30127                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
30128                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
30129                   ptrd+=sxy;
30130                   ptrs+=*(poff++);
30131                 }
30132               }
30133             }
30134           }
30135           resy.assign();
30136         } else resz.assign(resy,true);
30137 
30138         if (sc!=_spectrum) {
30139           if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
30140           else {
30141             if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
30142             else {
30143               const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.0)/(sc - 1):0):
30144                 (double)_spectrum/sc;
30145               const unsigned int sxyz = sx*sy*sz;
30146               resc.assign(sx,sy,sz,sc);
30147               curr = old = 0;
30148               unsigned int *poff = off._data;
30149               double *pfoff = foff._data;
30150               cimg_forC(resc,c) {
30151                 *(pfoff++) = curr - (unsigned int)curr;
30152                 old = curr;
30153                 curr = std::min(spectrum() - 1.0,curr + fc);
30154                 *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
30155               }
30156               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(resc.size()>=65536))
30157               cimg_forXYZ(resc,x,y,z) {
30158                 const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz,
30159                   *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
30160                 T *ptrd = resc.data(x,y,z,0);
30161                 const unsigned int *poff = off._data;
30162                 const double *pfoff = foff._data;
30163                 cimg_forC(resc,c) {
30164                   const double
30165                     t = *(pfoff++),
30166                     w0 = _cimg_lanczos(t + 2),
30167                     w1 = _cimg_lanczos(t + 1),
30168                     w2 = _cimg_lanczos(t),
30169                     w3 = _cimg_lanczos(t - 1),
30170                     w4 = _cimg_lanczos(t - 2),
30171                     val2 = (double)*ptrs,
30172                     val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2,
30173                     val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1,
30174                     val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2,
30175                     val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val3,
30176                     val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
30177                   *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
30178                   ptrd+=sxyz;
30179                   ptrs+=*(poff++);
30180                 }
30181               }
30182             }
30183           }
30184           resz.assign();
30185         } else resc.assign(resz,true);
30186 
30187         return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
30188       } break;
30189 
30190         // Unknow interpolation.
30191         //
30192       default :
30193         throw CImgArgumentException(_cimg_instance
30194                                     "resize(): Invalid specified interpolation %d "
30195                                     "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | "
30196                                     "5=cubic | 6=lanczos }).",
30197                                     cimg_instance,
30198                                     interpolation_type);
30199       }
30200       return res;
30201     }
30202 
30203     //! Resize image to dimensions of another image.
30204     /**
30205        \param src Reference image used for dimensions.
30206        \param interpolation_type Interpolation method.
30207        \param boundary_conditions Boundary conditions.
30208        \param centering_x Set centering type (only if \p interpolation_type=0).
30209        \param centering_y Set centering type (only if \p interpolation_type=0).
30210        \param centering_z Set centering type (only if \p interpolation_type=0).
30211        \param centering_c Set centering type (only if \p interpolation_type=0).
30212      **/
30213     template<typename t>
30214     CImg<T>& resize(const CImg<t>& src,
30215                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
30216                     const float centering_x = 0, const float centering_y = 0,
30217                     const float centering_z = 0, const float centering_c = 0) {
30218       return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
30219                     centering_x,centering_y,centering_z,centering_c);
30220     }
30221 
30222     //! Resize image to dimensions of another image \newinstance.
30223     template<typename t>
30224     CImg<T> get_resize(const CImg<t>& src,
30225                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
30226                        const float centering_x = 0, const float centering_y = 0,
30227                        const float centering_z = 0, const float centering_c = 0) const {
30228       return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
30229                         centering_x,centering_y,centering_z,centering_c);
30230     }
30231 
30232     //! Resize image to dimensions of a display window.
30233     /**
30234        \param disp Reference display window used for dimensions.
30235        \param interpolation_type Interpolation method.
30236        \param boundary_conditions Boundary conditions.
30237        \param centering_x Set centering type (only if \p interpolation_type=0).
30238        \param centering_y Set centering type (only if \p interpolation_type=0).
30239        \param centering_z Set centering type (only if \p interpolation_type=0).
30240        \param centering_c Set centering type (only if \p interpolation_type=0).
30241      **/
30242     CImg<T>& resize(const CImgDisplay& disp,
30243                     const int interpolation_type=1, const unsigned int boundary_conditions=0,
30244                     const float centering_x = 0, const float centering_y = 0,
30245                     const float centering_z = 0, const float centering_c = 0) {
30246       return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
30247                     centering_x,centering_y,centering_z,centering_c);
30248     }
30249 
30250     //! Resize image to dimensions of a display window \newinstance.
30251     CImg<T> get_resize(const CImgDisplay& disp,
30252                        const int interpolation_type=1, const unsigned int boundary_conditions=0,
30253                        const float centering_x = 0, const float centering_y = 0,
30254                        const float centering_z = 0, const float centering_c = 0) const {
30255       return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
30256                         centering_x,centering_y,centering_z,centering_c);
30257     }
30258 
30259     //! Resize image to half-size along XY axes, using an optimized filter.
30260     CImg<T>& resize_halfXY() {
30261       return get_resize_halfXY().move_to(*this);
30262     }
30263 
30264     //! Resize image to half-size along XY axes, using an optimized filter \newinstance.
30265     CImg<T> get_resize_halfXY() const {
30266       if (is_empty()) return *this;
30267       static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
30268                                         0.1231940459f,  0.1935127547f, 0.1231940459f,
30269                                         0.07842776544f, 0.1231940459f, 0.07842776544f };
30270       CImg<T> I(9), res(_width/2,_height/2,_depth,_spectrum);
30271       T *ptrd = res._data;
30272       cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
30273         if (x%2 && y%2) *(ptrd++) = (T)
30274                           (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] +
30275                            I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] +
30276                            I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]);
30277       return res;
30278     }
30279 
30280     //! Resize image to double-size, using the Scale2X algorithm.
30281     /**
30282        \note Use anisotropic upscaling algorithm
30283        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
30284     **/
30285     CImg<T>& resize_doubleXY() {
30286       return get_resize_doubleXY().move_to(*this);
30287     }
30288 
30289     //! Resize image to double-size, using the Scale2X algorithm \newinstance.
30290     CImg<T> get_resize_doubleXY() const {
30291 #define _cimg_gs2x_for3(bound,i) \
30292  for (int i = 0, _p1##i = 0, \
30293       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
30294       _n1##i<(int)(bound) || i==--_n1##i; \
30295       _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
30296 
30297 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
30298   _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
30299    _p1##x = 0, \
30300    _n1##x = (int)( \
30301    (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
30302    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
30303    (I[7] = (T)(img)(0,_n1##y,z,c)),	\
30304    1>=(img)._width?(img).width() - 1:1); \
30305    (_n1##x<(img).width() && ( \
30306    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
30307    (I[5] = (T)(img)(_n1##x,y,z,c)), \
30308    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
30309    x==--_n1##x; \
30310    I[1] = I[2], \
30311    I[3] = I[4], I[4] = I[5], \
30312    I[7] = I[8], \
30313    _p1##x = x++, ++_n1##x)
30314 
30315       if (is_empty()) return *this;
30316       CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
30317       CImg_3x3(I,T);
30318       cimg_forZC(*this,z,c) {
30319         T
30320           *ptrd1 = res.data(0,0,z,c),
30321           *ptrd2 = ptrd1 + res._width;
30322         _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
30323           if (Icp!=Icn && Ipc!=Inc) {
30324             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
30325             *(ptrd1++) = Icp==Inc?Inc:Icc;
30326             *(ptrd2++) = Ipc==Icn?Ipc:Icc;
30327             *(ptrd2++) = Icn==Inc?Inc:Icc;
30328           } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
30329         }
30330       }
30331       return res;
30332     }
30333 
30334     //! Resize image to triple-size, using the Scale3X algorithm.
30335     /**
30336        \note Use anisotropic upscaling algorithm
30337        <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
30338     **/
30339     CImg<T>& resize_tripleXY() {
30340       return get_resize_tripleXY().move_to(*this);
30341     }
30342 
30343     //! Resize image to triple-size, using the Scale3X algorithm \newinstance.
30344     CImg<T> get_resize_tripleXY() const {
30345 #define _cimg_gs3x_for3(bound,i) \
30346  for (int i = 0, _p1##i = 0, \
30347       _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
30348       _n1##i<(int)(bound) || i==--_n1##i; \
30349       _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
30350 
30351 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
30352   _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
30353    _p1##x = 0, \
30354    _n1##x = (int)( \
30355    (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
30356    (I[3] = I[4] = (T)(img)(0,y,z,c)), \
30357    (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)),	\
30358    1>=(img)._width?(img).width() - 1:1); \
30359    (_n1##x<(img).width() && ( \
30360    (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
30361    (I[5] = (T)(img)(_n1##x,y,z,c)), \
30362    (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
30363    x==--_n1##x; \
30364    I[0] = I[1], I[1] = I[2], \
30365    I[3] = I[4], I[4] = I[5], \
30366    I[6] = I[7], I[7] = I[8], \
30367    _p1##x = x++, ++_n1##x)
30368 
30369       if (is_empty()) return *this;
30370       CImg<T> res(3*_width,3*_height,_depth,_spectrum);
30371       CImg_3x3(I,T);
30372       cimg_forZC(*this,z,c) {
30373         T
30374           *ptrd1 = res.data(0,0,z,c),
30375           *ptrd2 = ptrd1 + res._width,
30376           *ptrd3 = ptrd2 + res._width;
30377         _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
30378           if (Icp != Icn && Ipc != Inc) {
30379             *(ptrd1++) = Ipc==Icp?Ipc:Icc;
30380             *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
30381             *(ptrd1++) = Icp==Inc?Inc:Icc;
30382             *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
30383             *(ptrd2++) = Icc;
30384             *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
30385             *(ptrd3++) = Ipc==Icn?Ipc:Icc;
30386             *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
30387             *(ptrd3++) = Icn==Inc?Inc:Icc;
30388           } else {
30389             *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
30390             *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
30391             *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
30392           }
30393         }
30394       }
30395       return res;
30396     }
30397 
30398     //! Mirror image content along specified axis.
30399     /**
30400        \param axis Mirror axis
30401     **/
30402     CImg<T>& mirror(const char axis) {
30403       if (is_empty()) return *this;
30404       T *pf, *pb, *buf = 0;
30405       switch (cimg::lowercase(axis)) {
30406       case 'x' : {
30407         pf = _data; pb = data(_width - 1);
30408         const unsigned int width2 = _width/2;
30409         for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
30410           for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
30411           pf+=_width - width2;
30412           pb+=_width + width2;
30413         }
30414       } break;
30415       case 'y' : {
30416         buf = new T[_width];
30417         pf = _data; pb = data(0,_height - 1);
30418         const unsigned int height2 = _height/2;
30419         for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
30420           for (unsigned int y = 0; y<height2; ++y) {
30421             std::memcpy(buf,pf,_width*sizeof(T));
30422             std::memcpy(pf,pb,_width*sizeof(T));
30423             std::memcpy(pb,buf,_width*sizeof(T));
30424             pf+=_width;
30425             pb-=_width;
30426           }
30427           pf+=(ulongT)_width*(_height - height2);
30428           pb+=(ulongT)_width*(_height + height2);
30429         }
30430       } break;
30431       case 'z' : {
30432         buf = new T[(ulongT)_width*_height];
30433         pf = _data; pb = data(0,0,_depth - 1);
30434         const unsigned int depth2 = _depth/2;
30435         cimg_forC(*this,c) {
30436           for (unsigned int z = 0; z<depth2; ++z) {
30437             std::memcpy(buf,pf,_width*_height*sizeof(T));
30438             std::memcpy(pf,pb,_width*_height*sizeof(T));
30439             std::memcpy(pb,buf,_width*_height*sizeof(T));
30440             pf+=(ulongT)_width*_height;
30441             pb-=(ulongT)_width*_height;
30442           }
30443           pf+=(ulongT)_width*_height*(_depth - depth2);
30444           pb+=(ulongT)_width*_height*(_depth + depth2);
30445         }
30446       } break;
30447       case 'c' : {
30448         buf = new T[(ulongT)_width*_height*_depth];
30449         pf = _data; pb = data(0,0,0,_spectrum - 1);
30450         const unsigned int _spectrum2 = _spectrum/2;
30451         for (unsigned int v = 0; v<_spectrum2; ++v) {
30452           std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
30453           std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
30454           std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
30455           pf+=(ulongT)_width*_height*_depth;
30456           pb-=(ulongT)_width*_height*_depth;
30457         }
30458       } break;
30459       default :
30460         throw CImgArgumentException(_cimg_instance
30461                                     "mirror(): Invalid specified axis '%c'.",
30462                                     cimg_instance,
30463                                     axis);
30464       }
30465       delete[] buf;
30466       return *this;
30467     }
30468 
30469     //! Mirror image content along specified axis \newinstance.
30470     CImg<T> get_mirror(const char axis) const {
30471       return (+*this).mirror(axis);
30472     }
30473 
30474     //! Mirror image content along specified axes.
30475     /**
30476        \param axes Mirror axes, as a C-string.
30477        \note \c axes may contains multiple characters, e.g. \c "xyz"
30478     **/
30479     CImg<T>& mirror(const char *const axes) {
30480       for (const char *s = axes; *s; ++s) mirror(*s);
30481       return *this;
30482     }
30483 
30484     //! Mirror image content along specified axes \newinstance.
30485     CImg<T> get_mirror(const char *const axes) const {
30486       return (+*this).mirror(axes);
30487     }
30488 
30489     //! Shift image content.
30490     /**
30491        \param delta_x Amount of displacement along the X-axis.
30492        \param delta_y Amount of displacement along the Y-axis.
30493        \param delta_z Amount of displacement along the Z-axis.
30494        \param delta_c Amount of displacement along the C-axis.
30495        \param boundary_conditions Border condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
30496     **/
30497     CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
30498                    const unsigned int boundary_conditions=0) {
30499       if (is_empty()) return *this;
30500       if (boundary_conditions==3)
30501         return get_crop(-delta_x,-delta_y,-delta_z,-delta_c,
30502                         width() - delta_x - 1,
30503                         height() - delta_y - 1,
30504                         depth() - delta_z - 1,
30505                         spectrum() - delta_c - 1,3).move_to(*this);
30506       if (delta_x) // Shift along X-axis
30507         switch (boundary_conditions) {
30508         case 2 : { // Periodic
30509           const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
30510           if (!ndelta_x) return *this;
30511           CImg<T> buf(cimg::abs(ndelta_x));
30512           if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
30513               std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
30514               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
30515               std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
30516             } else cimg_forYZC(*this,y,z,c) {
30517               std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T));
30518               std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T));
30519               std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
30520             }
30521         } break;
30522         case 1 : // Neumann
30523           if (delta_x<0) {
30524             const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x;
30525             if (!ndelta_x) return *this;
30526             cimg_forYZC(*this,y,z,c) {
30527               std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
30528               T *ptrd = data(_width - 1,y,z,c);
30529               const T val = *ptrd;
30530               for (int l = 0; l<ndelta_x - 1; ++l) *(--ptrd) = val;
30531             }
30532           } else {
30533             const int ndelta_x = (delta_x>=width())?width() - 1:delta_x;
30534             if (!ndelta_x) return *this;
30535             cimg_forYZC(*this,y,z,c) {
30536               std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
30537               T *ptrd = data(0,y,z,c);
30538               const T val = *ptrd;
30539               for (int l = 0; l<ndelta_x - 1; ++l) *(++ptrd) = val;
30540             }
30541           }
30542           break;
30543         default : // Dirichlet
30544           if (delta_x<=-width() || delta_x>=width()) return fill((T)0);
30545           if (delta_x<0) cimg_forYZC(*this,y,z,c) {
30546               std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T));
30547               std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T));
30548             } else cimg_forYZC(*this,y,z,c) {
30549               std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
30550               std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
30551             }
30552         }
30553 
30554       if (delta_y) // Shift along Y-axis
30555         switch (boundary_conditions) {
30556         case 2 : { // Periodic
30557           const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
30558           if (!ndelta_y) return *this;
30559           CImg<T> buf(width(),cimg::abs(ndelta_y));
30560           if (ndelta_y>0) cimg_forZC(*this,z,c) {
30561               std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
30562               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
30563               std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
30564             } else cimg_forZC(*this,z,c) {
30565               std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
30566               std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T));
30567               std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
30568             }
30569         } break;
30570         case 1 : // Neumann
30571           if (delta_y<0) {
30572             const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y;
30573             if (!ndelta_y) return *this;
30574             cimg_forZC(*this,z,c) {
30575               std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
30576               T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c);
30577               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
30578             }
30579           } else {
30580             const int ndelta_y = (delta_y>=height())?height() - 1:delta_y;
30581             if (!ndelta_y) return *this;
30582             cimg_forZC(*this,z,c) {
30583               std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
30584               T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
30585               for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
30586             }
30587           }
30588           break;
30589         default : // Dirichlet
30590           if (delta_y<=-height() || delta_y>=height()) return fill((T)0);
30591           if (delta_y<0) cimg_forZC(*this,z,c) {
30592               std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T));
30593               std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T));
30594             } else cimg_forZC(*this,z,c) {
30595               std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
30596               std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
30597             }
30598         }
30599 
30600       if (delta_z) // Shift along Z-axis
30601         switch (boundary_conditions) {
30602         case 2 : { // Periodic
30603           const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
30604           if (!ndelta_z) return *this;
30605           CImg<T> buf(width(),height(),cimg::abs(ndelta_z));
30606           if (ndelta_z>0) cimg_forC(*this,c) {
30607               std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
30608               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
30609               std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
30610             } else cimg_forC(*this,c) {
30611               std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
30612               std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T));
30613               std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
30614             }
30615         } break;
30616         case 1 : // Neumann
30617           if (delta_z<0) {
30618             const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z;
30619             if (!ndelta_z) return *this;
30620             cimg_forC(*this,c) {
30621               std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
30622               T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c);
30623               for (int l = 0; l<ndelta_z - 1; ++l) {
30624                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
30625               }
30626             }
30627           } else {
30628             const int ndelta_z = (delta_z>=depth())?depth() - 1:delta_z;
30629             if (!ndelta_z) return *this;
30630             cimg_forC(*this,c) {
30631               std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
30632               T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
30633               for (int l = 0; l<ndelta_z - 1; ++l) {
30634                 std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
30635               }
30636             }
30637           }
30638           break;
30639         default : // Dirichlet
30640           if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0);
30641           if (delta_z<0) cimg_forC(*this,c) {
30642               std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T));
30643               std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
30644             } else cimg_forC(*this,c) {
30645               std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
30646               std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
30647             }
30648         }
30649 
30650       if (delta_c) // Shift along C-axis
30651         switch (boundary_conditions) {
30652         case 2 : { // Periodic
30653           const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
30654           if (!ndelta_c) return *this;
30655           CImg<T> buf(width(),height(),depth(),cimg::abs(ndelta_c));
30656           if (ndelta_c>0) {
30657             std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
30658             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
30659             std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
30660           } else {
30661             std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
30662             std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T));
30663             std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
30664           }
30665         } break;
30666         case 1 : // Neumann
30667           if (delta_c<0) {
30668             const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c;
30669             if (!ndelta_c) return *this;
30670             std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
30671             T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1);
30672             for (int l = 0; l<ndelta_c - 1; ++l) {
30673               std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
30674             }
30675           } else {
30676             const int ndelta_c = (delta_c>=spectrum())?spectrum() - 1:delta_c;
30677             if (!ndelta_c) return *this;
30678             std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
30679             T *ptrd = data(0,0,0,1);
30680             for (int l = 0; l<ndelta_c - 1; ++l) {
30681               std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
30682             }
30683           }
30684           break;
30685         default : // Dirichlet
30686           if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0);
30687           if (delta_c<0) {
30688             std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T));
30689             std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
30690           } else {
30691             std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
30692             std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
30693           }
30694         }
30695       return *this;
30696     }
30697 
30698     //! Shift image content \newinstance.
30699     CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
30700                       const unsigned int boundary_conditions=0) const {
30701       return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
30702     }
30703 
30704     //! Permute axes order.
30705     /**
30706        \param order Axes permutations, as a C-string of 4 characters.
30707        This function permutes image content regarding the specified axes permutation.
30708     **/
30709     CImg<T>& permute_axes(const char *const order) {
30710       return get_permute_axes(order).move_to(*this);
30711     }
30712 
30713     //! Permute axes order \newinstance.
30714     CImg<T> get_permute_axes(const char *const order) const {
30715       const T foo = (T)0;
30716       return _permute_axes(order,foo);
30717     }
30718 
30719     template<typename t>
30720     CImg<t> _permute_axes(const char *const order, const t&) const {
30721       if (is_empty() || !order) return CImg<t>(*this,false);
30722       CImg<t> res;
30723       const T* ptrs = _data;
30724       unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 };
30725       for (unsigned int l = 0; order[l]; ++l) {
30726         int c = cimg::lowercase(order[l]);
30727         if (c!='x' && c!='y' && c!='z' && c!='c') { *s_code = 4; break; }
30728         else { ++n_code[c%=4]; s_code[l] = c; }
30729       }
30730       if (*order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
30731         const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
30732         ulongT wh, whd;
30733         switch (code) {
30734         case 0x0123 : // xyzc
30735           return +*this;
30736         case 0x0132 : // xycz
30737           res.assign(_width,_height,_spectrum,_depth);
30738           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30739           cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
30740           break;
30741         case 0x0213 : // xzyc
30742           res.assign(_width,_depth,_height,_spectrum);
30743           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30744           cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
30745           break;
30746         case 0x0231 : // xzcy
30747           res.assign(_width,_depth,_spectrum,_height);
30748           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30749           cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
30750           break;
30751         case 0x0312 : // xcyz
30752           res.assign(_width,_spectrum,_height,_depth);
30753           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30754           cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
30755           break;
30756         case 0x0321 : // xczy
30757           res.assign(_width,_spectrum,_depth,_height);
30758           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30759           cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
30760           break;
30761         case 0x1023 : // yxzc
30762           res.assign(_height,_width,_depth,_spectrum);
30763           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30764           cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
30765           break;
30766         case 0x1032 : // yxcz
30767           res.assign(_height,_width,_spectrum,_depth);
30768           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30769           cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
30770           break;
30771         case 0x1203 : // yzxc
30772           res.assign(_height,_depth,_width,_spectrum);
30773           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30774           cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
30775           break;
30776         case 0x1230 : // yzcx
30777           res.assign(_height,_depth,_spectrum,_width);
30778           switch (_width) {
30779           case 1 : {
30780             t *ptr_r = res.data(0,0,0,0);
30781             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
30782               *(ptr_r++) = (t)*(ptrs++);
30783             }
30784           } break;
30785           case 2 : {
30786             t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
30787             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
30788               *(ptr_r++) = (t)ptrs[0];
30789               *(ptr_g++) = (t)ptrs[1];
30790               ptrs+=2;
30791             }
30792           } break;
30793           case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
30794             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);
30795             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
30796               *(ptr_r++) = (t)ptrs[0];
30797               *(ptr_g++) = (t)ptrs[1];
30798               *(ptr_b++) = (t)ptrs[2];
30799               ptrs+=3;
30800             }
30801           } break;
30802           case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
30803             t
30804               *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1),
30805               *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
30806             for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
30807               *(ptr_r++) = (t)ptrs[0];
30808               *(ptr_g++) = (t)ptrs[1];
30809               *(ptr_b++) = (t)ptrs[2];
30810               *(ptr_a++) = (t)ptrs[3];
30811               ptrs+=4;
30812             }
30813           } break;
30814           default : {
30815             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30816             cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
30817             return res;
30818           }
30819           }
30820           break;
30821         case 0x1302 : // ycxz
30822           res.assign(_height,_spectrum,_width,_depth);
30823           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30824           cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
30825           break;
30826         case 0x1320 : // yczx
30827           res.assign(_height,_spectrum,_depth,_width);
30828           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30829           cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
30830           break;
30831         case 0x2013 : // zxyc
30832           res.assign(_depth,_width,_height,_spectrum);
30833           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30834           cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
30835           break;
30836         case 0x2031 : // zxcy
30837           res.assign(_depth,_width,_spectrum,_height);
30838           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30839           cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
30840           break;
30841         case 0x2103 : // zyxc
30842           res.assign(_depth,_height,_width,_spectrum);
30843           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30844           cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
30845           break;
30846         case 0x2130 : // zycx
30847           res.assign(_depth,_height,_spectrum,_width);
30848           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30849           cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
30850           break;
30851         case 0x2301 : // zcxy
30852           res.assign(_depth,_spectrum,_width,_height);
30853           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30854           cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
30855           break;
30856         case 0x2310 : // zcyx
30857           res.assign(_depth,_spectrum,_height,_width);
30858           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30859           cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
30860           break;
30861         case 0x3012 : // cxyz
30862           res.assign(_spectrum,_width,_height,_depth);
30863           switch (_spectrum) {
30864           case 1 : {
30865             const T *ptr_r = data(0,0,0,0);
30866             t *ptrd = res._data;
30867             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
30868           } break;
30869           case 2 : {
30870             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
30871             t *ptrd = res._data;
30872             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
30873               ptrd[0] = (t)*(ptr_r++);
30874               ptrd[1] = (t)*(ptr_g++);
30875               ptrd+=2;
30876             }
30877           } break;
30878           case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
30879             const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
30880             t *ptrd = res._data;
30881             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
30882               ptrd[0] = (t)*(ptr_r++);
30883               ptrd[1] = (t)*(ptr_g++);
30884               ptrd[2] = (t)*(ptr_b++);
30885               ptrd+=3;
30886             }
30887           } break;
30888           case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
30889             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);
30890             t *ptrd = res._data;
30891             for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
30892               ptrd[0] = (t)*(ptr_r++);
30893               ptrd[1] = (t)*(ptr_g++);
30894               ptrd[2] = (t)*(ptr_b++);
30895               ptrd[3] = (t)*(ptr_a++);
30896               ptrd+=4;
30897             }
30898           } break;
30899           default : {
30900             wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30901             cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
30902           }
30903           }
30904           break;
30905         case 0x3021 : // cxzy
30906           res.assign(_spectrum,_width,_depth,_height);
30907           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30908           cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
30909           break;
30910         case 0x3102 : // cyxz
30911           res.assign(_spectrum,_height,_width,_depth);
30912           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30913           cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
30914           break;
30915         case 0x3120 : // cyzx
30916           res.assign(_spectrum,_height,_depth,_width);
30917           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30918           cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
30919           break;
30920         case 0x3201 : // czxy
30921           res.assign(_spectrum,_depth,_width,_height);
30922           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30923           cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
30924           break;
30925         case 0x3210 : // czyx
30926           res.assign(_spectrum,_depth,_height,_width);
30927           wh = (ulongT)res._width*res._height; whd = wh*res._depth;
30928           cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
30929           break;
30930         }
30931       }
30932       if (!res)
30933         throw CImgArgumentException(_cimg_instance
30934                                     "permute_axes(): Invalid specified permutation '%s'.",
30935                                     cimg_instance,
30936                                     order);
30937       return res;
30938     }
30939 
30940     //! Unroll pixel values along specified axis.
30941     /**
30942        \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c').
30943     **/
30944     CImg<T>& unroll(const char axis) {
30945       const unsigned int siz = (unsigned int)size();
30946       if (siz) switch (cimg::lowercase(axis)) {
30947       case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
30948       case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
30949       case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
30950       default : _spectrum = siz; _width = _height = _depth = 1;
30951       }
30952       return *this;
30953     }
30954 
30955     //! Unroll pixel values along specified axis \newinstance.
30956     CImg<T> get_unroll(const char axis) const {
30957       return (+*this).unroll(axis);
30958     }
30959 
30960     //! Rotate image with arbitrary angle.
30961     /**
30962        \param angle Rotation angle, in degrees.
30963        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
30964        \param boundary_conditions Boundary conditions.
30965               Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
30966        \note The size of the image is modified.
30967     **/
30968     CImg<T>& rotate(const float angle, const unsigned int interpolation=1,
30969                     const unsigned int boundary_conditions=0) {
30970       const float nangle = cimg::mod(angle,360.0f);
30971       if (nangle==0.0f) return *this;
30972       return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this);
30973     }
30974 
30975     //! Rotate image with arbitrary angle \newinstance.
30976     CImg<T> get_rotate(const float angle, const unsigned int interpolation=1,
30977                        const unsigned int boundary_conditions=0) const {
30978       if (is_empty()) return *this;
30979       CImg<T> res;
30980       const float nangle = cimg::mod(angle,360.0f);
30981       if (boundary_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // Optimized version for orthogonal angles.
30982         const int wm1 = width() - 1, hm1 = height() - 1;
30983         const int iangle = (int)nangle/90;
30984         switch (iangle) {
30985         case 1 : { // 90 deg
30986           res.assign(_height,_width,_depth,_spectrum);
30987           T *ptrd = res._data;
30988           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c);
30989         } break;
30990         case 2 : { // 180 deg
30991           res.assign(_width,_height,_depth,_spectrum);
30992           T *ptrd = res._data;
30993           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c);
30994         } break;
30995         case 3 : { // 270 deg
30996           res.assign(_height,_width,_depth,_spectrum);
30997           T *ptrd = res._data;
30998           cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c);
30999         } break;
31000         default : // 0 deg
31001           return *this;
31002         }
31003       } else { // Generic angle
31004         const float
31005           rad = (float)(nangle*cimg::PI/180.0),
31006           ca = (float)std::cos(rad), sa = (float)std::sin(rad),
31007           ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa),
31008           vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca),
31009           w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1);
31010         res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum);
31011         const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1);
31012         _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2);
31013       }
31014       return res;
31015     }
31016 
31017     //! Rotate image with arbitrary angle, around a center point.
31018     /**
31019        \param angle Rotation angle, in degrees.
31020        \param cx X-coordinate of the rotation center.
31021        \param cy Y-coordinate of the rotation center.
31022        \param interpolation Type of interpolation, <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
31023        \param boundary_conditions Boundary conditions, <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
31024     **/
31025     CImg<T>& rotate(const float angle, const float cx, const float cy,
31026                     const unsigned int interpolation, const unsigned int boundary_conditions=0) {
31027       return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this);
31028     }
31029 
31030     //! Rotate image with arbitrary angle, around a center point \newinstance.
31031     CImg<T> get_rotate(const float angle, const float cx, const float cy,
31032                        const unsigned int interpolation, const unsigned int boundary_conditions=0) const {
31033       if (is_empty()) return *this;
31034       CImg<T> res(_width,_height,_depth,_spectrum);
31035       _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy);
31036       return res;
31037     }
31038 
31039     // [internal] Perform 2d rotation with arbitrary angle.
31040     void _rotate(CImg<T>& res, const float angle,
31041                  const unsigned int interpolation, const unsigned int boundary_conditions,
31042                  const float w2, const float h2,
31043                  const float rw2, const float rh2) const {
31044       const float
31045         rad = (float)(angle*cimg::PI/180.0),
31046         ca = (float)std::cos(rad), sa = (float)std::sin(rad);
31047 
31048       switch (boundary_conditions) {
31049       case 3 : { // Mirror
31050 
31051         switch (interpolation) {
31052         case 2 : { // Cubic interpolation
31053           const float ww = 2.0f*width(), hh = 2.0f*height();
31054           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31055             cimg_forXYZC(res,x,y,z,c) {
31056             const float xc = x - rw2, yc = y - rh2,
31057               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
31058               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
31059             res(x,y,z,c) = _cubic_cut_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
31060           }
31061         } break;
31062         case 1 : { // Linear interpolation
31063           const float ww = 2.0f*width(), hh = 2.0f*height();
31064           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31065             cimg_forXYZC(res,x,y,z,c) {
31066             const float xc = x - rw2, yc = y - rh2,
31067               mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
31068               my = cimg::mod(h2 - xc*sa + yc*ca,hh);
31069             res(x,y,z,c) = (T)_linear_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
31070           }
31071         } break;
31072         default : { // Nearest-neighbor interpolation
31073           const int ww = 2*width(), hh = 2*height();
31074           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31075             cimg_forXYZC(res,x,y,z,c) {
31076             const float xc = x - rw2, yc = y - rh2,
31077               mx = cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),ww),
31078               my = cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),hh);
31079             res(x,y,z,c) = (*this)(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
31080           }
31081         }
31082         }
31083       } break;
31084 
31085       case 2 : // Periodic
31086         switch (interpolation) {
31087         case 2 : { // Cubic interpolation
31088           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31089             cimg_forXYZC(res,x,y,z,c) {
31090             const float xc = x - rw2, yc = y - rh2;
31091             res(x,y,z,c) = _cubic_cut_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()),
31092                                            cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c);
31093           }
31094         } break;
31095         case 1 : { // Linear interpolation
31096           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31097             cimg_forXYZC(res,x,y,z,c) {
31098             const float xc = x - rw2, yc = y - rh2;
31099             res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + xc*ca + yc*sa,(float)width()),
31100                                            cimg::mod(h2 - xc*sa + yc*ca,(float)height()),z,c);
31101           }
31102         } break;
31103         default : { // Nearest-neighbor interpolation
31104           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31105             cimg_forXYZC(res,x,y,z,c) {
31106             const float xc = x - rw2, yc = y - rh2;
31107             res(x,y,z,c) = (*this)(cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),(float)width()),
31108                                    cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),(float)height()),z,c);
31109           }
31110         }
31111         } break;
31112 
31113       case 1 : // Neumann
31114         switch (interpolation) {
31115         case 2 : { // Cubic interpolation
31116           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31117           cimg_forXYZC(res,x,y,z,c) {
31118             const float xc = x - rw2, yc = y - rh2;
31119             res(x,y,z,c) = _cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
31120           }
31121         } break;
31122         case 1 : { // Linear interpolation
31123           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31124           cimg_forXYZC(res,x,y,z,c) {
31125             const float xc = x - rw2, yc = y - rh2;
31126             res(x,y,z,c) = (T)_linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
31127           }
31128         } break;
31129         default : { // Nearest-neighbor interpolation
31130           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31131           cimg_forXYZC(res,x,y,z,c) {
31132             const float xc = x - rw2, yc = y - rh2;
31133             res(x,y,z,c) = _atXY((int)cimg::round(w2 + xc*ca + yc*sa),
31134                                  (int)cimg::round(h2 - xc*sa + yc*ca),z,c);
31135           }
31136         }
31137         } break;
31138 
31139       default : // Dirichlet
31140         switch (interpolation) {
31141         case 2 : { // Cubic interpolation
31142           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31143           cimg_forXYZC(res,x,y,z,c) {
31144             const float xc = x - rw2, yc = y - rh2;
31145             res(x,y,z,c) = cubic_cut_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
31146           }
31147         } break;
31148         case 1 : { // Linear interpolation
31149           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31150           cimg_forXYZC(res,x,y,z,c) {
31151             const float xc = x - rw2, yc = y - rh2;
31152             res(x,y,z,c) = (T)linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
31153           }
31154         } break;
31155         default : { // Nearest-neighbor interpolation
31156           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=2048))
31157           cimg_forXYZC(res,x,y,z,c) {
31158             const float xc = x - rw2, yc = y - rh2;
31159             res(x,y,z,c) = atXY((int)cimg::round(w2 + xc*ca + yc*sa),
31160                                 (int)cimg::round(h2 - xc*sa + yc*ca),z,c,(T)0);
31161           }
31162         }
31163         }
31164       }
31165     }
31166 
31167     //! Rotate volumetric image with arbitrary angle and axis.
31168     /**
31169        \param u X-coordinate of the 3d rotation axis.
31170        \param v Y-coordinate of the 3d rotation axis.
31171        \param w Z-coordinate of the 3d rotation axis.
31172        \param angle Rotation angle, in degrees.
31173        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
31174        \param boundary_conditions Boundary conditions.
31175               Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
31176        \note Most of the time, size of the image is modified.
31177     **/
31178     CImg<T> rotate(const float u, const float v, const float w, const float angle,
31179                    const unsigned int interpolation, const unsigned int boundary_conditions) {
31180       const float nangle = cimg::mod(angle,360.0f);
31181       if (nangle==0.0f) return *this;
31182       return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this);
31183     }
31184 
31185     //! Rotate volumetric image with arbitrary angle and axis \newinstance.
31186     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
31187                        const unsigned int interpolation, const unsigned int boundary_conditions) const {
31188       if (is_empty()) return *this;
31189       CImg<T> res;
31190       const float
31191         w1 = _width - 1, h1 = _height - 1, d1 = _depth -1,
31192         w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1;
31193       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,angle);
31194       const CImg<Tfloat>
31195         X = R*CImg<Tfloat>(8,3,1,1,
31196                            0.0f,w1,w1,0.0f,0.0f,w1,w1,0.0f,
31197                            0.0f,0.0f,h1,h1,0.0f,0.0f,h1,h1,
31198                            0.0f,0.0f,0.0f,0.0f,d1,d1,d1,d1);
31199       float
31200         xm, xM = X.get_shared_row(0).max_min(xm),
31201         ym, yM = X.get_shared_row(1).max_min(ym),
31202         zm, zM = X.get_shared_row(2).max_min(zm);
31203       const int
31204         dx = (int)cimg::round(xM - xm),
31205         dy = (int)cimg::round(yM - ym),
31206         dz = (int)cimg::round(zM - zm);
31207       R.transpose();
31208       res.assign(1 + dx,1 + dy,1 + dz,_spectrum);
31209       const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz;
31210       _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2);
31211       return res;
31212     }
31213 
31214     //! Rotate volumetric image with arbitrary angle and axis, around a center point.
31215     /**
31216        \param u X-coordinate of the 3d rotation axis.
31217        \param v Y-coordinate of the 3d rotation axis.
31218        \param w Z-coordinate of the 3d rotation axis.
31219        \param angle Rotation angle, in degrees.
31220        \param cx X-coordinate of the rotation center.
31221        \param cy Y-coordinate of the rotation center.
31222        \param cz Z-coordinate of the rotation center.
31223        \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
31224        \param boundary_conditions Boundary conditions. Can be <tt>{  0=dirichlet | 1=neumann | 2=periodic }</tt>.
31225        \note Most of the time, size of the image is modified.
31226     **/
31227     CImg<T> rotate(const float u, const float v, const float w, const float angle,
31228                    const float cx, const float cy, const float cz,
31229                    const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
31230       const float nangle = cimg::mod(angle,360.0f);
31231       if (nangle==0.0f) return *this;
31232       return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this);
31233     }
31234 
31235     //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance.
31236     CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
31237                        const float cx, const float cy, const float cz,
31238                        const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
31239       if (is_empty()) return *this;
31240       CImg<T> res(_width,_height,_depth,_spectrum);
31241       CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,-angle);
31242       _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz);
31243       return res;
31244     }
31245 
31246     // [internal] Perform 3d rotation with arbitrary axis and angle.
31247     void _rotate(CImg<T>& res, const CImg<Tfloat>& R,
31248                  const unsigned int interpolation, const unsigned int boundary_conditions,
31249                  const float w2, const float h2, const float d2,
31250                  const float rw2, const float rh2, const float rd2) const {
31251       switch (boundary_conditions) {
31252       case 3 : // Mirror
31253         switch (interpolation) {
31254         case 2 : { // Cubic interpolation
31255           const float ww = 2.0f*width(), hh = 2.0f*height(), dd = 2.0f*depth();
31256           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31257           cimg_forXYZ(res,x,y,z) {
31258             const float
31259               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31260               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
31261               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
31262               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
31263             cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X<width()?X:ww - X - 1,
31264                                                              Y<height()?Y:hh - Y - 1,
31265                                                              Z<depth()?Z:dd - Z - z,c);
31266           }
31267         } break;
31268         case 1 : { // Linear interpolation
31269           const float ww = 2.0f*width(), hh = 2.0f*height(), dd = 2.0f*depth();
31270           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31271           cimg_forXYZ(res,x,y,z) {
31272             const float
31273               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31274               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
31275               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
31276               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
31277             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X<width()?X:ww - X - 1,
31278                                                              Y<height()?Y:hh - Y - 1,
31279                                                              Z<depth()?Z:dd - Z - 1,c);
31280           }
31281         } break;
31282         default : { // Nearest-neighbor interpolation
31283           const int ww = 2*width(), hh = 2*height(), dd = 2*depth();
31284           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31285           cimg_forXYZ(res,x,y,z) {
31286             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
31287             const int
31288               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
31289               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
31290               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
31291             cimg_forC(res,c) res(x,y,z,c) = (*this)(X<width()?X:ww - X - 1,
31292                                                     Y<height()?Y:hh - Y - 1,
31293                                                     Z<depth()?Z:dd - Z -  1,c);
31294           }
31295         }
31296         } break;
31297 
31298       case 2 : // Periodic
31299         switch (interpolation) {
31300         case 2 : { // Cubic interpolation
31301           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31302           cimg_forXYZ(res,x,y,z) {
31303             const float
31304               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31305               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),(float)width()),
31306               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),(float)height()),
31307               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),(float)depth());
31308             cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c);
31309           }
31310         } break;
31311         case 1 : { // Linear interpolation
31312           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31313           cimg_forXYZ(res,x,y,z) {
31314             const float
31315               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31316               X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),(float)width()),
31317               Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),(float)height()),
31318               Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),(float)depth());
31319             cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X,Y,Z,c);
31320           }
31321         } break;
31322         default : { // Nearest-neighbor interpolation
31323           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31324           cimg_forXYZ(res,x,y,z) {
31325             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
31326             const int
31327               X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),width()),
31328               Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),height()),
31329               Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),depth());
31330             cimg_forC(res,c) res(x,y,z,c) = (*this)(X,Y,Z,c);
31331           }
31332         }
31333         } break;
31334 
31335       case 1 : // Neumann
31336         switch (interpolation) {
31337         case 2 : { // Cubic interpolation
31338           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31339           cimg_forXYZ(res,x,y,z) {
31340             const float
31341               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31342               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
31343               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
31344               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
31345             cimg_forC(res,c) res(x,y,z,c) = _cubic_cut_atXYZ(X,Y,Z,c);
31346           }
31347         } break;
31348         case 1 : { // Linear interpolation
31349           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31350           cimg_forXYZ(res,x,y,z) {
31351             const float
31352               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31353               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
31354               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
31355               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
31356             cimg_forC(res,c) res(x,y,z,c) = _linear_atXYZ(X,Y,Z,c);
31357           }
31358         } break;
31359         default : { // Nearest-neighbor interpolation
31360           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31361           cimg_forXYZ(res,x,y,z) {
31362             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
31363             const int
31364               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
31365               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
31366               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
31367             cimg_forC(res,c) res(x,y,z,c) = _atXYZ(X,Y,Z,c);
31368           }
31369         }
31370         } break;
31371 
31372       default : // Dirichlet
31373         switch (interpolation) {
31374         case 2 : { // Cubic interpolation
31375           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31376           cimg_forXYZ(res,x,y,z) {
31377             const float
31378               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31379               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
31380               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
31381               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
31382             cimg_forC(res,c) res(x,y,z,c) = cubic_cut_atXYZ(X,Y,Z,c,(T)0);
31383           }
31384         } break;
31385         case 1 : { // Linear interpolation
31386           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31387           cimg_forXYZ(res,x,y,z) {
31388             const float
31389               xc = x - rw2, yc = y - rh2, zc = z - rd2,
31390               X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
31391               Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
31392               Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
31393             cimg_forC(res,c) res(x,y,z,c) = linear_atXYZ(X,Y,Z,c,(T)0);
31394           }
31395         } break;
31396         default : { // Nearest-neighbor interpolation
31397           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res.size()>=2048))
31398           cimg_forXYZ(res,x,y,z) {
31399             const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
31400             const int
31401               X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
31402               Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
31403               Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
31404             cimg_forC(res,c) res(x,y,z,c) = atXYZ(X,Y,Z,c,(T)0);
31405           }
31406         }
31407         } break;
31408       }
31409     }
31410 
31411     //! Warp image content by a warping field.
31412     /**
31413        \param warp Warping field.
31414        \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative }
31415        \param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
31416        \param boundary_conditions Boundary conditions <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
31417     **/
31418     template<typename t>
31419     CImg<T>& warp(const CImg<t>& warp, const unsigned int mode=0,
31420                   const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
31421       return get_warp(warp,mode,interpolation,boundary_conditions).move_to(*this);
31422     }
31423 
31424     //! Warp image content by a warping field \newinstance
31425     template<typename t>
31426     CImg<T> get_warp(const CImg<t>& warp, const unsigned int mode=0,
31427                      const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
31428       if (is_empty() || !warp) return *this;
31429       if (mode && !is_sameXYZ(warp))
31430         throw CImgArgumentException(_cimg_instance
31431                                     "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
31432                                     "have different XYZ dimensions.",
31433                                     cimg_instance,
31434                                     warp._width,warp._height,warp._depth,warp._spectrum,warp._data);
31435 
31436       CImg<T> res(warp._width,warp._height,warp._depth,_spectrum);
31437 
31438       if (warp._spectrum==1) { // 1d warping
31439         if (mode>=3) { // Forward-relative warp
31440           res.fill((T)0);
31441           if (interpolation>=1) // Linear interpolation
31442             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31443             cimg_forYZC(res,y,z,c) {
31444               const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
31445               cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c);
31446             }
31447           else // Nearest-neighbor interpolation
31448             cimg_forYZC(res,y,z,c) {
31449               const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
31450               cimg_forX(res,x) {
31451                 const int X = x + (int)cimg::round(*(ptrs0++));
31452                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
31453               }
31454             }
31455         } else if (mode==2) { // Forward-absolute warp
31456           res.fill((T)0);
31457           if (interpolation>=1) // Linear interpolation
31458             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31459             cimg_forYZC(res,y,z,c) {
31460               const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
31461               cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c);
31462             }
31463           else // Nearest-neighbor interpolation
31464             cimg_forYZC(res,y,z,c) {
31465               const t *ptrs0 = warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
31466               cimg_forX(res,x) {
31467                 const int X = (int)cimg::round(*(ptrs0++));
31468                 if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
31469               }
31470             }
31471         } else if (mode==1) { // Backward-relative warp
31472           if (interpolation==2) // Cubic interpolation
31473             switch (boundary_conditions) {
31474             case 3 : { // Mirror
31475               const float w2 = 2.0f*width();
31476               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31477               cimg_forYZC(res,y,z,c) {
31478                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31479                 cimg_forX(res,x) {
31480                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
31481                   *(ptrd++) = _cubic_cut_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
31482                 }
31483               }
31484             } break;
31485             case 2 : // Periodic
31486               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31487               cimg_forYZC(res,y,z,c) {
31488                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31489                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
31490               }
31491               break;
31492             case 1 : // Neumann
31493               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31494               cimg_forYZC(res,y,z,c) {
31495                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31496                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(x - (float)*(ptrs0++),y,z,c);
31497               }
31498               break;
31499             default : // Dirichlet
31500               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31501               cimg_forYZC(res,y,z,c) {
31502                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31503                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
31504               }
31505             }
31506           else if (interpolation==1) // Linear interpolation
31507             switch (boundary_conditions) {
31508             case 3 : { // Mirror
31509               const float w2 = 2.0f*width();
31510               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31511               cimg_forYZC(res,y,z,c) {
31512                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31513                 cimg_forX(res,x) {
31514                   const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
31515                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
31516                 }
31517               }
31518             } break;
31519             case 2 : // Periodic
31520               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31521               cimg_forYZC(res,y,z,c) {
31522                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31523                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
31524               }
31525               break;
31526             case 1 : // Neumann
31527               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31528               cimg_forYZC(res,y,z,c) {
31529                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31530                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
31531               }
31532               break;
31533             default : // Dirichlet
31534               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31535               cimg_forYZC(res,y,z,c) {
31536                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31537                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
31538               }
31539             }
31540           else // Nearest-neighbor interpolation
31541             switch (boundary_conditions) {
31542             case 3 : { // Mirror
31543               const int w2 = 2*width();
31544               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31545               cimg_forYZC(res,y,z,c) {
31546                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31547                 cimg_forX(res,x) {
31548                   const int mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2);
31549                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,y,z,c);
31550                 }
31551               }
31552             } break;
31553             case 2 : // Periodic
31554               cimg_forYZC(res,y,z,c) {
31555                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31556                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),y,z,c);
31557               }
31558               break;
31559             case 1 : // Neumann
31560               cimg_forYZC(res,y,z,c) {
31561                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31562                 cimg_forX(res,x) *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c);
31563               }
31564               break;
31565             default : // Dirichlet
31566               cimg_forYZC(res,y,z,c) {
31567                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31568                 cimg_forX(res,x) *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,(T)0);
31569               }
31570             }
31571         }
31572         else { // Backward-absolute warp
31573           if (interpolation==2) // Cubic interpolation
31574             switch (boundary_conditions) {
31575             case 3 : { // Mirror
31576               const float w2 = 2.0f*width();
31577               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31578                 cimg_forYZC(res,y,z,c) {
31579                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31580                 cimg_forX(res,x) {
31581                   const float mx = cimg::mod((float)*(ptrs0++),w2);
31582                   *(ptrd++) = _cubic_cut_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
31583                 }
31584               }
31585             } break;
31586             case 2 : // Periodic
31587               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31588               cimg_forYZC(res,y,z,c) {
31589                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31590                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
31591               }
31592               break;
31593             case 1 : // Neumann
31594               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31595               cimg_forYZC(res,y,z,c) {
31596                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31597                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atX((float)*(ptrs0++),0,0,c);
31598               }
31599               break;
31600             default : // Dirichlet
31601               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31602               cimg_forYZC(res,y,z,c) {
31603                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31604                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atX((float)*(ptrs0++),0,0,c,(T)0);
31605               }
31606             }
31607           else if (interpolation==1) // Linear interpolation
31608             switch (boundary_conditions) {
31609             case 3 : { // Mirror
31610               const float w2 = 2.0f*width();
31611               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31612                 cimg_forYZC(res,y,z,c) {
31613                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31614                 cimg_forX(res,x) {
31615                   const float mx = cimg::mod((float)*(ptrs0++),w2);
31616                   *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
31617                 }
31618               }
31619             } break;
31620             case 2 : // Periodic
31621               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31622               cimg_forYZC(res,y,z,c) {
31623                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31624                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
31625               }
31626               break;
31627             case 1 : // Neumann
31628               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31629               cimg_forYZC(res,y,z,c) {
31630                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31631                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
31632               }
31633               break;
31634             default : // Dirichlet
31635               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31636               cimg_forYZC(res,y,z,c) {
31637                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31638                 cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,(T)0);
31639               }
31640             }
31641           else // Nearest-neighbor interpolation
31642             switch (boundary_conditions) {
31643             case 3 : { // Mirror
31644               const int w2 = 2*width();
31645               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31646                 cimg_forYZC(res,y,z,c) {
31647                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31648                 cimg_forX(res,x) {
31649                   const int mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2);
31650                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,0,0,c);
31651                 }
31652               }
31653             } break;
31654             case 2 : // Periodic
31655               cimg_forYZC(res,y,z,c) {
31656                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31657                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),0,0,c);
31658               }
31659               break;
31660             case 1 : // Neumann
31661               cimg_forYZC(res,y,z,c) {
31662                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31663                 cimg_forX(res,x) *(ptrd++) = _atX((int)*(ptrs0++),0,0,c);
31664               }
31665               break;
31666             default : // Dirichlet
31667               cimg_forYZC(res,y,z,c) {
31668                 const t *ptrs0 = warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
31669                 cimg_forX(res,x) *(ptrd++) = atX((int)*(ptrs0++),0,0,c,(T)0);
31670               }
31671             }
31672         }
31673 
31674       } else if (warp._spectrum==2) { // 2d warping
31675         if (mode>=3) { // Forward-relative warp
31676           res.fill((T)0);
31677           if (interpolation>=1) // Linear interpolation
31678             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31679             cimg_forYZC(res,y,z,c) {
31680               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
31681               cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c);
31682             }
31683           else // Nearest-neighbor interpolation
31684             cimg_forYZC(res,y,z,c) {
31685               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
31686               cimg_forX(res,x) {
31687                 const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++));
31688                 if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
31689               }
31690             }
31691         } else if (mode==2) { // Forward-absolute warp
31692           res.fill((T)0);
31693           if (interpolation>=1) // Linear interpolation
31694             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31695             cimg_forYZC(res,y,z,c) {
31696               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
31697               cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c);
31698             }
31699           else // Nearest-neighbor interpolation
31700             cimg_forYZC(res,y,z,c) {
31701               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
31702               cimg_forX(res,x) {
31703                 const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++));
31704                 if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
31705               }
31706             }
31707         } else if (mode==1) { // Backward-relative warp
31708           if (interpolation==2) // Cubic interpolation
31709             switch (boundary_conditions) {
31710             case 3 : { // Mirror
31711               const float w2 = 2.0f*width(), h2 = 2.0f*height();
31712               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31713               cimg_forYZC(res,y,z,c) {
31714                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31715                 cimg_forX(res,x) {
31716                   const float
31717                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
31718                     my = cimg::mod(y - (float)*(ptrs1++),h2);
31719                   *(ptrd++) = _cubic_cut_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
31720                 }
31721               }
31722             } break;
31723             case 2 : // Periodic
31724               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31725               cimg_forYZC(res,y,z,c) {
31726                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31727                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
31728                                                              cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
31729               }
31730               break;
31731             case 1 : // Neumann
31732               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31733               cimg_forYZC(res,y,z,c) {
31734                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31735                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
31736               }
31737               break;
31738             default : // Dirichlet
31739               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31740               cimg_forYZC(res,y,z,c) {
31741                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31742                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
31743               }
31744             }
31745           else if (interpolation==1) // Linear interpolation
31746             switch (boundary_conditions) {
31747             case 3 : { // Mirror
31748               const float w2 = 2.0f*width(), h2 = 2.0f*height();
31749               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31750               cimg_forYZC(res,y,z,c) {
31751                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31752                 cimg_forX(res,x) {
31753                   const float
31754                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
31755                     my = cimg::mod(y - (float)*(ptrs1++),h2);
31756                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
31757                 }
31758               }
31759             } break;
31760             case 2 : // Periodic
31761               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31762               cimg_forYZC(res,y,z,c) {
31763                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31764                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
31765                                                              cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
31766               }
31767               break;
31768             case 1 : // Neumann
31769               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31770               cimg_forYZC(res,y,z,c) {
31771                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31772                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
31773               }
31774               break;
31775             default : // Dirichlet
31776               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31777               cimg_forYZC(res,y,z,c) {
31778                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31779                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
31780               }
31781             }
31782           else // Nearest-neighbor interpolation
31783             switch (boundary_conditions) {
31784             case 3 : { // Mirror
31785               const int w2 = 2*width(), h2 = 2*height();
31786               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31787               cimg_forYZC(res,y,z,c) {
31788                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31789                 cimg_forX(res,x) {
31790                   const int
31791                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
31792                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2);
31793                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
31794                 }
31795               }
31796             } break;
31797             case 2 : // Periodic
31798               cimg_forYZC(res,y,z,c) {
31799                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31800                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),
31801                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),(int)_height),z,c);
31802               }
31803               break;
31804             case 1 : // Neumann
31805               cimg_forYZC(res,y,z,c) {
31806                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31807                 cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c);
31808               }
31809               break;
31810             default : // Dirichlet
31811               cimg_forYZC(res,y,z,c) {
31812                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31813                 cimg_forX(res,x) *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,(T)0);
31814               }
31815             }
31816         } else { // Backward-absolute warp
31817           if (interpolation==2) // Cubic interpolation
31818             switch (boundary_conditions) {
31819             case 3 : { // Mirror
31820               const float w2 = 2.0f*width(), h2 = 2.0f*height();
31821               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31822               cimg_forYZC(res,y,z,c) {
31823                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31824                 cimg_forX(res,x) {
31825                   const float
31826                     mx = cimg::mod((float)*(ptrs0++),w2),
31827                     my = cimg::mod((float)*(ptrs1++),h2);
31828                   *(ptrd++) = _cubic_cut_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
31829                 }
31830               }
31831             } break;
31832             case 2 : // Periodic
31833               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31834               cimg_forYZC(res,y,z,c) {
31835                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31836                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
31837                                                              cimg::mod((float)*(ptrs1++),(float)_height),0,c);
31838               }
31839               break;
31840             case 1 : // Neumann
31841               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31842               cimg_forYZC(res,y,z,c) {
31843                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31844                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
31845               }
31846               break;
31847             default : // Dirichlet
31848               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31849               cimg_forYZC(res,y,z,c) {
31850                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31851                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
31852               }
31853             }
31854           else if (interpolation==1) // Linear interpolation
31855             switch (boundary_conditions) {
31856             case 3 : { // Mirror
31857               const float w2 = 2.0f*width(), h2 = 2.0f*height();
31858               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31859               cimg_forYZC(res,y,z,c) {
31860                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31861                 cimg_forX(res,x) {
31862                   const float
31863                     mx = cimg::mod((float)*(ptrs0++),w2),
31864                     my = cimg::mod((float)*(ptrs1++),h2);
31865                   *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
31866                 }
31867               }
31868             } break;
31869             case 2 : // Periodic
31870               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31871               cimg_forYZC(res,y,z,c) {
31872                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31873                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
31874                                                              cimg::mod((float)*(ptrs1++),(float)_height),0,c);
31875               }
31876               break;
31877             case 1 : // Neumann
31878               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31879               cimg_forYZC(res,y,z,c) {
31880                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31881                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
31882               }
31883               break;
31884             default : // Dirichlet
31885               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
31886               cimg_forYZC(res,y,z,c) {
31887                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31888                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
31889               }
31890             }
31891           else // Nearest-neighbor interpolation
31892             switch (boundary_conditions) {
31893             case 3 : { // Mirror
31894               const int w2 = 2*width(), h2 = 2*height();
31895               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31896               cimg_forYZC(res,y,z,c) {
31897                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31898                 cimg_forX(res,x) {
31899                   const int
31900                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
31901                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2);
31902                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
31903                 }
31904               }
31905             } break;
31906             case 2 : // Periodic
31907               cimg_forYZC(res,y,z,c) {
31908                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31909                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),
31910                                                      cimg::mod((int)cimg::round(*(ptrs1++)),(int)_height),0,c);
31911               }
31912               break;
31913             case 1 : // Neumann
31914               cimg_forYZC(res,y,z,c) {
31915                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31916                 cimg_forX(res,x) *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c);
31917               }
31918               break;
31919             default : // Dirichlet
31920               cimg_forYZC(res,y,z,c) {
31921                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
31922                 cimg_forX(res,x) *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,(T)0);
31923               }
31924             }
31925         }
31926 
31927       } else { // 3d warping
31928         if (mode>=3) { // Forward-relative warp
31929           res.fill((T)0);
31930           if (interpolation>=1) // Linear interpolation
31931             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31932             cimg_forYZC(res,y,z,c) {
31933               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31934               const T *ptrs = data(0,y,z,c);
31935               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),
31936                                                     z + (float)*(ptrs2++),c);
31937             }
31938           else // Nearest-neighbor interpolation
31939             cimg_forYZC(res,y,z,c) {
31940               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31941               const T *ptrs = data(0,y,z,c);
31942               cimg_forX(res,x) {
31943                 const int
31944                   X = x + (int)cimg::round(*(ptrs0++)),
31945                   Y = y + (int)cimg::round(*(ptrs1++)),
31946                   Z = z + (int)cimg::round(*(ptrs2++));
31947                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
31948               }
31949             }
31950         } else if (mode==2) { // Forward-absolute warp
31951           res.fill((T)0);
31952           if (interpolation>=1) // Linear interpolation
31953             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31954             cimg_forYZC(res,y,z,c) {
31955               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31956               const T *ptrs = data(0,y,z,c);
31957               cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
31958             }
31959           else // Nearest-neighbor interpolation
31960             cimg_forYZC(res,y,z,c) {
31961               const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31962               const T *ptrs = data(0,y,z,c);
31963               cimg_forX(res,x) {
31964                 const int
31965                   X = (int)cimg::round(*(ptrs0++)),
31966                   Y = (int)cimg::round(*(ptrs1++)),
31967                   Z = (int)cimg::round(*(ptrs2++));
31968                 if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
31969               }
31970             }
31971         } else if (mode==1) { // Backward-relative warp
31972           if (interpolation==2) // Cubic interpolation
31973             switch (boundary_conditions) {
31974             case 3 : { // Mirror
31975               const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
31976               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31977               cimg_forYZC(res,y,z,c) {
31978                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31979                 T *ptrd = res.data(0,y,z,c);
31980                 cimg_forX(res,x) {
31981                   const float
31982                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
31983                     my = cimg::mod(y - (float)*(ptrs1++),h2),
31984                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
31985                   *(ptrd++) = _cubic_cut_atXYZ(mx<width()?mx:w2 - mx - 1,
31986                                                my<height()?my:h2 - my - 1,
31987                                                mz<depth()?mz:d2 - mz - 1,c);
31988                 }
31989               }
31990             } break;
31991             case 2 : // Periodic
31992               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
31993               cimg_forYZC(res,y,z,c) {
31994                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
31995                 T *ptrd = res.data(0,y,z,c);
31996                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
31997                                                               cimg::mod(y - (float)*(ptrs1++),(float)_height),
31998                                                               cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
31999               }
32000               break;
32001             case 1 : // Neumann
32002               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32003               cimg_forYZC(res,y,z,c) {
32004                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32005                 T *ptrd = res.data(0,y,z,c);
32006                 cimg_forX(res,x)
32007                   *(ptrd++) = _cubic_cut_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
32008               }
32009               break;
32010             default : // Dirichlet
32011               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32012               cimg_forYZC(res,y,z,c) {
32013                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32014                 T *ptrd = res.data(0,y,z,c);
32015                 cimg_forX(res,x)
32016                   *(ptrd++) = cubic_cut_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
32017               }
32018             }
32019           else if (interpolation==1) // Linear interpolation
32020             switch (boundary_conditions) {
32021             case 3 : { // Mirror
32022               const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
32023               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32024               cimg_forYZC(res,y,z,c) {
32025                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32026                 T *ptrd = res.data(0,y,z,c);
32027                 cimg_forX(res,x) {
32028                   const float
32029                     mx = cimg::mod(x - (float)*(ptrs0++),w2),
32030                     my = cimg::mod(y - (float)*(ptrs1++),h2),
32031                     mz = cimg::mod(z - (float)*(ptrs2++),d2);
32032                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
32033                                                my<height()?my:h2 - my - 1,
32034                                                mz<depth()?mz:d2 - mz - 1,c);
32035                 }
32036               }
32037             } break;
32038             case 2 : // Periodic
32039               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32040               cimg_forYZC(res,y,z,c) {
32041                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32042                 T *ptrd = res.data(0,y,z,c);
32043                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
32044                                                               cimg::mod(y - (float)*(ptrs1++),(float)_height),
32045                                                               cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
32046               }
32047               break;
32048             case 1 : // Neumann
32049               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32050               cimg_forYZC(res,y,z,c) {
32051                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32052                 T *ptrd = res.data(0,y,z,c);
32053                 cimg_forX(res,x)
32054                   *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
32055               }
32056               break;
32057             default : // Dirichlet
32058               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32059               cimg_forYZC(res,y,z,c) {
32060                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32061                 T *ptrd = res.data(0,y,z,c);
32062                 cimg_forX(res,x)
32063                   *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
32064               }
32065             }
32066           else // Nearest neighbor interpolation
32067             switch (boundary_conditions) {
32068             case 3 : { // Mirror
32069               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
32070               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32071               cimg_forYZC(res,y,z,c) {
32072                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32073                 T *ptrd = res.data(0,y,z,c);
32074                 cimg_forX(res,x) {
32075                   const int
32076                     mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
32077                     my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2),
32078                     mz = cimg::mod(z - (int)cimg::round(*(ptrs2++)),d2);
32079                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
32080                                       my<height()?my:h2 - my - 1,
32081                                       mz<depth()?mz:d2 - mz - 1,c);
32082                 }
32083               }
32084             } break;
32085             case 2 : // Periodic
32086               cimg_forYZC(res,y,z,c) {
32087                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32088                 T *ptrd = res.data(0,y,z,c);
32089                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),(int)_width),
32090                                                      cimg::mod(y - (int)cimg::round(*(ptrs1++)),(int)_height),
32091                                                      cimg::mod(z - (int)cimg::round(*(ptrs2++)),(int)_depth),c);
32092               }
32093               break;
32094             case 1 : // Neumann
32095               cimg_forYZC(res,y,z,c) {
32096                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32097                 T *ptrd = res.data(0,y,z,c);
32098                 cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c);
32099               }
32100               break;
32101             default : // Dirichlet
32102               cimg_forYZC(res,y,z,c) {
32103                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32104                 T *ptrd = res.data(0,y,z,c);
32105                 cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,(T)0);
32106               }
32107             }
32108         } else { // Backward-absolute warp
32109           if (interpolation==2) // Cubic interpolation
32110             switch (boundary_conditions) {
32111             case 3 : { // Mirror
32112               const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
32113               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32114               cimg_forYZC(res,y,z,c) {
32115                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32116                 T *ptrd = res.data(0,y,z,c);
32117                 cimg_forX(res,x) {
32118                   const float
32119                     mx = cimg::mod((float)*(ptrs0++),w2),
32120                     my = cimg::mod((float)*(ptrs1++),h2),
32121                     mz = cimg::mod((float)*(ptrs2++),d2);
32122                   *(ptrd++) = _cubic_cut_atXYZ(mx<width()?mx:w2 - mx - 1,
32123                                                my<height()?my:h2 - my - 1,
32124                                                mz<depth()?mz:d2 - mz - 1,c);
32125                 }
32126               }
32127             } break;
32128             case 2 : // Periodic
32129               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32130               cimg_forYZC(res,y,z,c) {
32131                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32132                 T *ptrd = res.data(0,y,z,c);
32133                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
32134                                                               cimg::mod((float)*(ptrs1++),(float)_height),
32135                                                               cimg::mod((float)*(ptrs2++),(float)_depth),c);
32136               }
32137               break;
32138             case 1 : // Neumann
32139               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32140               cimg_forYZC(res,y,z,c) {
32141                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32142                 T *ptrd = res.data(0,y,z,c);
32143                 cimg_forX(res,x) *(ptrd++) = _cubic_cut_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
32144               }
32145               break;
32146             default : // Dirichlet
32147               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32148               cimg_forYZC(res,y,z,c) {
32149                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32150                 T *ptrd = res.data(0,y,z,c);
32151                 cimg_forX(res,x) *(ptrd++) = cubic_cut_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
32152                                                              c,(T)0);
32153               }
32154             }
32155           else if (interpolation==1) // Linear interpolation
32156             switch (boundary_conditions) {
32157             case 3 : { // Mirror
32158               const float w2 = 2.0f*width(), h2 = 2.0f*height(), d2 = 2.0f*depth();
32159               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32160               cimg_forYZC(res,y,z,c) {
32161                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32162                 T *ptrd = res.data(0,y,z,c);
32163                 cimg_forX(res,x) {
32164                   const float
32165                     mx = cimg::mod((float)*(ptrs0++),w2),
32166                     my = cimg::mod((float)*(ptrs1++),h2),
32167                     mz = cimg::mod((float)*(ptrs2++),d2);
32168                   *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
32169                                                my<height()?my:h2 - my - 1,
32170                                                mz<depth()?mz:d2 - mz - 1,c);
32171                 }
32172               }
32173             } break;
32174             case 2 :// Periodic
32175               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32176               cimg_forYZC(res,y,z,c) {
32177                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32178                 T *ptrd = res.data(0,y,z,c);
32179                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
32180                                                               cimg::mod((float)*(ptrs1++),(float)_height),
32181                                                               cimg::mod((float)*(ptrs2++),(float)_depth),c);
32182               }
32183               break;
32184             case 1 : // Neumann
32185               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32186               cimg_forYZC(res,y,z,c) {
32187                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32188                 T *ptrd = res.data(0,y,z,c);
32189                 cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
32190               }
32191               break;
32192             default : // Dirichlet
32193               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=1048576))
32194               cimg_forYZC(res,y,z,c) {
32195                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32196                 T *ptrd = res.data(0,y,z,c);
32197                 cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
32198                                                              c,(T)0);
32199               }
32200             }
32201           else // Nearest-neighbor interpolation
32202             switch (boundary_conditions) {
32203             case 3 : { // Mirror
32204               const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
32205               cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(res.size()>=4096))
32206               cimg_forYZC(res,y,z,c) {
32207                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32208                 T *ptrd = res.data(0,y,z,c);
32209                 cimg_forX(res,x) {
32210                   const int
32211                     mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
32212                     my = cimg::mod((int)cimg::round(*(ptrs1++)),h2),
32213                     mz = cimg::mod((int)cimg::round(*(ptrs2++)),d2);
32214                   *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
32215                                       my<height()?my:h2 - my - 1,
32216                                       mz<depth()?mz:d2 - mz - 1,c);
32217                 }
32218               }
32219             } break;
32220             case 2 : // Periodic
32221               cimg_forYZC(res,y,z,c) {
32222                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32223                 T *ptrd = res.data(0,y,z,c);
32224                 cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),(int)_width),
32225                                                      cimg::mod((int)cimg::round(*(ptrs1++)),(int)_height),
32226                                                      cimg::mod((int)cimg::round(*(ptrs2++)),(int)_depth),c);
32227               }
32228               break;
32229             case 1 : // Neumann
32230               cimg_forYZC(res,y,z,c) {
32231                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32232                 T *ptrd = res.data(0,y,z,c);
32233                 cimg_forX(res,x) *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c);
32234               }
32235               break;
32236             default : // Dirichlet
32237               cimg_forYZC(res,y,z,c) {
32238                 const t *ptrs0 = warp.data(0,y,z,0), *ptrs1 = warp.data(0,y,z,1), *ptrs2 = warp.data(0,y,z,2);
32239                 T *ptrd = res.data(0,y,z,c);
32240                 cimg_forX(res,x) *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,(T)0);
32241               }
32242             }
32243         }
32244       }
32245       return res;
32246     }
32247 
32248     //! Generate a 2d representation of a 3d image, with XY,XZ and YZ views.
32249     /**
32250        \param x0 X-coordinate of the projection point.
32251        \param y0 Y-coordinate of the projection point.
32252        \param z0 Z-coordinate of the projection point.
32253     **/
32254     CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
32255       if (is_empty() || _depth<2) return +*this;
32256       const unsigned int
32257         _x0 = (x0>=_width)?_width - 1:x0,
32258         _y0 = (y0>=_height)?_height - 1:y0,
32259         _z0 = (z0>=_depth)?_depth - 1:z0;
32260       const CImg<T>
32261         img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1),
32262         img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc").
32263         resize(_depth,_height,1,-100,-1),
32264         img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1);
32265       return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
32266         draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
32267         draw_image(0,img_xy._height,img_xz);
32268     }
32269 
32270     //! Construct a 2d representation of a 3d image, with XY,XZ and YZ views \inplace.
32271     CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
32272       if (_depth<2) return *this;
32273       return get_projections2d(x0,y0,z0).move_to(*this);
32274     }
32275 
32276     //! Crop image region.
32277     /**
32278        \param x0 = X-coordinate of the upper-left crop rectangle corner.
32279        \param y0 = Y-coordinate of the upper-left crop rectangle corner.
32280        \param z0 = Z-coordinate of the upper-left crop rectangle corner.
32281        \param c0 = C-coordinate of the upper-left crop rectangle corner.
32282        \param x1 = X-coordinate of the lower-right crop rectangle corner.
32283        \param y1 = Y-coordinate of the lower-right crop rectangle corner.
32284        \param z1 = Z-coordinate of the lower-right crop rectangle corner.
32285        \param c1 = C-coordinate of the lower-right crop rectangle corner.
32286        \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
32287     **/
32288     CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
32289                   const int x1, const int y1, const int z1, const int c1,
32290                   const unsigned int boundary_conditions=0) {
32291       return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
32292     }
32293 
32294     //! Crop image region \newinstance.
32295     CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
32296                      const int x1, const int y1, const int z1, const int c1,
32297                      const unsigned int boundary_conditions=0) const {
32298       if (is_empty())
32299         throw CImgInstanceException(_cimg_instance
32300                                     "crop(): Empty instance.",
32301                                     cimg_instance);
32302       const int
32303         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
32304         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
32305         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
32306         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
32307       CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
32308       if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum())
32309         switch (boundary_conditions) {
32310         case 3 : { // Mirror
32311           const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
32312           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
32313           cimg_forXYZC(res,x,y,z,c) {
32314             const int
32315               mx = cimg::mod(nx0 + x,w2),
32316               my = cimg::mod(ny0 + y,h2),
32317               mz = cimg::mod(nz0 + z,d2),
32318               mc = cimg::mod(nc0 + c,s2);
32319             res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
32320                                    my<height()?my:h2 - my - 1,
32321                                    mz<depth()?mz:d2 - mz - 1,
32322                                    mc<spectrum()?mc:s2 - mc - 1);
32323           }
32324         } break;
32325         case 2 : { // Periodic
32326           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
32327           cimg_forXYZC(res,x,y,z,c) {
32328             res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()),
32329                                    cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum()));
32330           }
32331         } break;
32332         case 1 : // Neumann
32333           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
32334           cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c);
32335           break;
32336         default : // Dirichlet
32337           res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
32338         }
32339       else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
32340       return res;
32341     }
32342 
32343     //! Crop image region \overloading.
32344     CImg<T>& crop(const int x0, const int y0, const int z0,
32345                   const int x1, const int y1, const int z1,
32346                   const unsigned int boundary_conditions=0) {
32347       return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
32348     }
32349 
32350     //! Crop image region \newinstance.
32351     CImg<T> get_crop(const int x0, const int y0, const int z0,
32352                      const int x1, const int y1, const int z1,
32353                      const unsigned int boundary_conditions=0) const {
32354       return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
32355     }
32356 
32357     //! Crop image region \overloading.
32358     CImg<T>& crop(const int x0, const int y0,
32359                   const int x1, const int y1,
32360                   const unsigned int boundary_conditions=0) {
32361       return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
32362     }
32363 
32364     //! Crop image region \newinstance.
32365     CImg<T> get_crop(const int x0, const int y0,
32366                      const int x1, const int y1,
32367                      const unsigned int boundary_conditions=0) const {
32368       return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
32369     }
32370 
32371     //! Crop image region \overloading.
32372     CImg<T>& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) {
32373       return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
32374     }
32375 
32376     //! Crop image region \newinstance.
32377     CImg<T> get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const {
32378       return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
32379     }
32380 
32381     //! Autocrop image region, regarding the specified background value.
32382     CImg<T>& autocrop(const T& value, const char *const axes="czyx") {
32383       if (is_empty()) return *this;
32384       for (const char *s = axes; *s; ++s) {
32385         const char axis = cimg::lowercase(*s);
32386         const CImg<intT> coords = _autocrop(value,axis);
32387         if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels.
32388         else switch (axis) {
32389         case 'x' : {
32390 	  const int x0 = coords[0], x1 = coords[1];
32391 	  if (x0>=0 && x1>=0) crop(x0,x1);
32392 	} break;
32393         case 'y' : {
32394 	  const int y0 = coords[0], y1 = coords[1];
32395 	  if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1);
32396 	} break;
32397         case 'z' : {
32398 	  const int z0 = coords[0], z1 = coords[1];
32399 	  if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1);
32400 	} break;
32401         default : {
32402 	  const int c0 = coords[0], c1 = coords[1];
32403 	  if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1);
32404 	}
32405         }
32406       }
32407       return *this;
32408     }
32409 
32410     //! Autocrop image region, regarding the specified background value \newinstance.
32411     CImg<T> get_autocrop(const T& value, const char *const axes="czyx") const {
32412       return (+*this).autocrop(value,axes);
32413     }
32414 
32415     //! Autocrop image region, regarding the specified background color.
32416     /**
32417        \param color Color used for the crop. If \c 0, color is guessed.
32418        \param axes Axes used for the crop.
32419     **/
32420     CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
32421       if (is_empty()) return *this;
32422       if (!color) { // Guess color.
32423         const CImg<T> col1 = get_vector_at(0,0,0);
32424         const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
32425         autocrop(col1,axes);
32426         if (_width==w && _height==h && _depth==d && _spectrum==s) {
32427           const CImg<T> col2 = get_vector_at(w - 1,h - 1,d - 1);
32428           autocrop(col2,axes);
32429         }
32430         return *this;
32431       }
32432       for (const char *s = axes; *s; ++s) {
32433         const char axis = cimg::lowercase(*s);
32434         switch (axis) {
32435         case 'x' : {
32436 	  int x0 = width(), x1 = -1;
32437 	  cimg_forC(*this,c) {
32438 	    const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
32439 	    const int nx0 = coords[0], nx1 = coords[1];
32440 	    if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); }
32441 	  }
32442           if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
32443 	} break;
32444         case 'y' : {
32445 	  int y0 = height(), y1 = -1;
32446 	  cimg_forC(*this,c) {
32447 	    const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
32448 	    const int ny0 = coords[0], ny1 = coords[1];
32449 	    if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); }
32450 	  }
32451           if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1);
32452 	} break;
32453         default : {
32454 	  int z0 = depth(), z1 = -1;
32455 	  cimg_forC(*this,c) {
32456 	    const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
32457 	    const int nz0 = coords[0], nz1 = coords[1];
32458 	    if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); }
32459 	  }
32460 	  if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1);
32461 	}
32462         }
32463       }
32464       return *this;
32465     }
32466 
32467     //! Autocrop image region, regarding the specified background color \newinstance.
32468     CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
32469       return (+*this).autocrop(color,axes);
32470     }
32471 
32472     //! Autocrop image region, regarding the specified background color \overloading.
32473     template<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") {
32474       return get_autocrop(color,axes).move_to(*this);
32475     }
32476 
32477     //! Autocrop image region, regarding the specified background color \newinstance.
32478     template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char *const axes="zyx") const {
32479       return get_autocrop(color._data,axes);
32480     }
32481 
32482     CImg<intT> _autocrop(const T& value, const char axis) const {
32483       CImg<intT> res;
32484       switch (cimg::lowercase(axis)) {
32485       case 'x' : {
32486         int x0 = -1, x1 = -1;
32487         cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
32488           if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
32489 	if (x0>=0) {
32490           for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c)
32491             if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
32492         }
32493 	res = CImg<intT>::vector(x0,x1);
32494       } break;
32495       case 'y' : {
32496         int y0 = -1, y1 = -1;
32497         cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
32498           if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
32499 	if (y0>=0) {
32500           for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c)
32501             if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
32502         }
32503   	res = CImg<intT>::vector(y0,y1);
32504       } break;
32505       case 'z' : {
32506         int z0 = -1, z1 = -1;
32507         cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
32508           if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
32509 	if (z0>=0) {
32510           for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c)
32511             if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
32512         }
32513   	res = CImg<intT>::vector(z0,z1);
32514       } break;
32515       default : {
32516         int c0 = -1, c1 = -1;
32517         cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
32518           if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
32519 	if (c0>=0) {
32520           for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
32521             if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
32522         }
32523   	res = CImg<intT>::vector(c0,c1);
32524       }
32525       }
32526       return res;
32527     }
32528 
32529     //! Return specified image column.
32530     /**
32531        \param x0 Image column.
32532     **/
32533     CImg<T> get_column(const int x0) const {
32534       return get_columns(x0,x0);
32535     }
32536 
32537     //! Return specified image column \inplace.
32538     CImg<T>& column(const int x0) {
32539       return columns(x0,x0);
32540     }
32541 
32542     //! Return specified range of image columns.
32543     /**
32544        \param x0 Starting image column.
32545        \param x1 Ending image column.
32546     **/
32547     CImg<T>& columns(const int x0, const int x1) {
32548       return get_columns(x0,x1).move_to(*this);
32549     }
32550 
32551     //! Return specified range of image columns \inplace.
32552     CImg<T> get_columns(const int x0, const int x1) const {
32553       return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1);
32554     }
32555 
32556     //! Return specified image row.
32557     CImg<T> get_row(const int y0) const {
32558       return get_rows(y0,y0);
32559     }
32560 
32561     //! Return specified image row \inplace.
32562     /**
32563        \param y0 Image row.
32564     **/
32565     CImg<T>& row(const int y0) {
32566       return rows(y0,y0);
32567     }
32568 
32569     //! Return specified range of image rows.
32570     /**
32571        \param y0 Starting image row.
32572        \param y1 Ending image row.
32573     **/
32574     CImg<T> get_rows(const int y0, const int y1) const {
32575       return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1);
32576     }
32577 
32578     //! Return specified range of image rows \inplace.
32579     CImg<T>& rows(const int y0, const int y1) {
32580       return get_rows(y0,y1).move_to(*this);
32581     }
32582 
32583     //! Return specified image slice.
32584     /**
32585        \param z0 Image slice.
32586     **/
32587     CImg<T> get_slice(const int z0) const {
32588       return get_slices(z0,z0);
32589     }
32590 
32591     //! Return specified image slice \inplace.
32592     CImg<T>& slice(const int z0) {
32593       return slices(z0,z0);
32594     }
32595 
32596     //! Return specified range of image slices.
32597     /**
32598        \param z0 Starting image slice.
32599        \param z1 Ending image slice.
32600     **/
32601     CImg<T> get_slices(const int z0, const int z1) const {
32602       return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1);
32603     }
32604 
32605     //! Return specified range of image slices \inplace.
32606     CImg<T>& slices(const int z0, const int z1) {
32607       return get_slices(z0,z1).move_to(*this);
32608     }
32609 
32610     //! Return specified image channel.
32611     /**
32612        \param c0 Image channel.
32613     **/
32614     CImg<T> get_channel(const int c0) const {
32615       return get_channels(c0,c0);
32616     }
32617 
32618     //! Return specified image channel \inplace.
32619     CImg<T>& channel(const int c0) {
32620       return channels(c0,c0);
32621     }
32622 
32623     //! Return specified range of image channels.
32624     /**
32625        \param c0 Starting image channel.
32626        \param c1 Ending image channel.
32627     **/
32628     CImg<T> get_channels(const int c0, const int c1) const {
32629       return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1);
32630     }
32631 
32632     //! Return specified range of image channels \inplace.
32633     CImg<T>& channels(const int c0, const int c1) {
32634       return get_channels(c0,c1).move_to(*this);
32635     }
32636 
32637     //! Return stream line of a 2d or 3d vector field.
32638     CImg<floatT> get_streamline(const float x, const float y, const float z,
32639                                 const float L=256, const float dl=0.1f,
32640                                 const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
32641                                 const bool is_oriented_only=false) const {
32642       if (_spectrum!=2 && _spectrum!=3)
32643         throw CImgInstanceException(_cimg_instance
32644                                     "streamline(): Instance is not a 2d or 3d vector field.",
32645                                     cimg_instance);
32646       if (_spectrum==2) {
32647         if (is_oriented_only) {
32648           typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
32649           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
32650                             0,0,0,_width - 1.0f,_height - 1.0f,0.0f);
32651         } else {
32652           typename CImg<T>::_functor4d_streamline2d_directed func(*this);
32653           return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
32654                             0,0,0,_width - 1.0f,_height - 1.0f,0.0f);
32655         }
32656       }
32657       if (is_oriented_only) {
32658         typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
32659         return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
32660                           0,0,0,_width - 1.0f,_height - 1.0f,_depth - 1.0f);
32661       }
32662       typename CImg<T>::_functor4d_streamline3d_directed func(*this);
32663       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
32664                         0,0,0,_width - 1.0f,_height - 1.0f,_depth - 1.0f);
32665     }
32666 
32667     //! Return stream line of a 3d vector field.
32668     /**
32669        \param func Vector field function.
32670        \param x X-coordinate of the starting point of the streamline.
32671        \param y Y-coordinate of the starting point of the streamline.
32672        \param z Z-coordinate of the starting point of the streamline.
32673        \param L Streamline length.
32674        \param dl Streamline length increment.
32675        \param interpolation_type Type of interpolation.
32676          Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>.
32677        \param is_backward_tracking Tells if the streamline is estimated forward or backward.
32678        \param is_oriented_only Tells if the direction of the vectors must be ignored.
32679        \param x0 X-coordinate of the first bounding-box vertex.
32680        \param y0 Y-coordinate of the first bounding-box vertex.
32681        \param z0 Z-coordinate of the first bounding-box vertex.
32682        \param x1 X-coordinate of the second bounding-box vertex.
32683        \param y1 Y-coordinate of the second bounding-box vertex.
32684        \param z1 Z-coordinate of the second bounding-box vertex.
32685     **/
32686     template<typename tfunc>
32687     static CImg<floatT> streamline(const tfunc& func,
32688                                    const float x, const float y, const float z,
32689                                    const float L=256, const float dl=0.1f,
32690                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
32691                                    const bool is_oriented_only=false,
32692                                    const float x0=0, const float y0=0, const float z0=0,
32693                                    const float x1=0, const float y1=0, const float z1=0) {
32694       if (dl<=0)
32695         throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
32696                                     "(should be >0).",
32697                                     pixel_type(),
32698                                     dl);
32699 
32700       const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
32701       if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
32702       const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1);
32703       CImg<floatT> coordinates(size_L,3);
32704       const float dl2 = dl/2;
32705       float
32706         *ptr_x = coordinates.data(0,0),
32707         *ptr_y = coordinates.data(0,1),
32708         *ptr_z = coordinates.data(0,2),
32709         pu = (float)(dl*func(x,y,z,0)),
32710         pv = (float)(dl*func(x,y,z,1)),
32711         pw = (float)(dl*func(x,y,z,2)),
32712         X = x, Y = y, Z = z;
32713 
32714       switch (interpolation_type) {
32715       case 0 : { // Nearest integer interpolation.
32716         cimg_forX(coordinates,l) {
32717           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
32718           const int
32719             xi = (int)(X>0?X + 0.5f:X - 0.5f),
32720             yi = (int)(Y>0?Y + 0.5f:Y - 0.5f),
32721             zi = (int)(Z>0?Z + 0.5f:Z - 0.5f);
32722           float
32723             u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
32724             v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
32725             w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
32726           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
32727           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
32728           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
32729         }
32730       } break;
32731       case 1 : { // First-order interpolation.
32732         cimg_forX(coordinates,l) {
32733           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
32734           float
32735             u = (float)(dl*func(X,Y,Z,0)),
32736             v = (float)(dl*func(X,Y,Z,1)),
32737             w = (float)(dl*func(X,Y,Z,2));
32738           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
32739           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
32740           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
32741         }
32742       } break;
32743       case 2 : { // Second order interpolation.
32744         cimg_forX(coordinates,l) {
32745           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
32746           float
32747             u0 = (float)(dl2*func(X,Y,Z,0)),
32748             v0 = (float)(dl2*func(X,Y,Z,1)),
32749             w0 = (float)(dl2*func(X,Y,Z,2));
32750           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
32751           float
32752             u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)),
32753             v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)),
32754             w = (float)(dl*func(X + u0,Y + v0,Z + w0,2));
32755           if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
32756           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
32757           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
32758         }
32759       } break;
32760       default : { // Fourth order interpolation.
32761         cimg_forX(coordinates,x) {
32762           *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
32763           float
32764             u0 = (float)(dl2*func(X,Y,Z,0)),
32765             v0 = (float)(dl2*func(X,Y,Z,1)),
32766             w0 = (float)(dl2*func(X,Y,Z,2));
32767           if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
32768           float
32769             u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)),
32770             v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)),
32771             w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2));
32772           if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
32773           float
32774             u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)),
32775             v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)),
32776             w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2));
32777           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
32778           float
32779             u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)),
32780             v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)),
32781             w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2));
32782           if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
32783           const float
32784             u = (u0 + u3)/3 + (u1 + u2)/1.5f,
32785             v = (v0 + v3)/3 + (v1 + v2)/1.5f,
32786             w = (w0 + w3)/3 + (w1 + w2)/1.5f;
32787           if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
32788           if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
32789         }
32790       }
32791       }
32792       if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
32793       return coordinates;
32794     }
32795 
32796     //! Return stream line of a 3d vector field \overloading.
32797     static CImg<floatT> streamline(const char *const expression,
32798                                    const float x, const float y, const float z,
32799                                    const float L=256, const float dl=0.1f,
32800                                    const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
32801                                    const bool is_oriented_only=false,
32802                                    const float x0=0, const float y0=0, const float z0=0,
32803                                    const float x1=0, const float y1=0, const float z1=0) {
32804       _functor4d_streamline_expr func(expression);
32805       return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
32806     }
32807 
32808     struct _functor4d_streamline2d_directed {
32809       const CImg<T>& ref;
32810       _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
32811       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32812         return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
32813       }
32814     };
32815 
32816     struct _functor4d_streamline3d_directed {
32817       const CImg<T>& ref;
32818       _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
32819       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32820         return (float)ref._linear_atXYZ(x,y,z,c);
32821       }
32822     };
32823 
32824     struct _functor4d_streamline2d_oriented {
32825       const CImg<T>& ref;
32826       CImg<floatT> *pI;
32827       _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
32828       ~_functor4d_streamline2d_oriented() { delete pI; }
32829       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32830 #define _cimg_vecalign2d(i,j) \
32831         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); }
32832         int
32833           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
32834           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
32835           zi = (int)z;
32836         const float
32837           dx = x - xi,
32838           dy = y - yi;
32839         if (c==0) {
32840           CImg<floatT>& I = *pI;
32841           if (xi<0) xi = 0;
32842           if (nxi<0) nxi = 0;
32843           if (xi>=ref.width()) xi = ref.width() - 1;
32844           if (nxi>=ref.width()) nxi = ref.width() - 1;
32845           if (yi<0) yi = 0;
32846           if (nyi<0) nyi = 0;
32847           if (yi>=ref.height()) yi = ref.height() - 1;
32848           if (nyi>=ref.height()) nyi = ref.height() - 1;
32849           I(0,0,0) = (float)ref(xi,yi,zi,0);   I(0,0,1) = (float)ref(xi,yi,zi,1);
32850           I(1,0,0) = (float)ref(nxi,yi,zi,0);  I(1,0,1) = (float)ref(nxi,yi,zi,1);
32851           I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
32852           I(0,1,0) = (float)ref(xi,nyi,zi,0);  I(0,1,1) = (float)ref(xi,nyi,zi,1);
32853           _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
32854         }
32855         return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
32856       }
32857     };
32858 
32859     struct _functor4d_streamline3d_oriented {
32860       const CImg<T>& ref;
32861       CImg<floatT> *pI;
32862       _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
32863       ~_functor4d_streamline3d_oriented() { delete pI; }
32864       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32865 #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) { \
32866   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); }
32867         int
32868           xi = (int)x - (x>=0?0:1), nxi = xi + 1,
32869           yi = (int)y - (y>=0?0:1), nyi = yi + 1,
32870           zi = (int)z - (z>=0?0:1), nzi = zi + 1;
32871         const float
32872           dx = x - xi,
32873           dy = y - yi,
32874           dz = z - zi;
32875         if (c==0) {
32876           CImg<floatT>& I = *pI;
32877           if (xi<0) xi = 0;
32878           if (nxi<0) nxi = 0;
32879           if (xi>=ref.width()) xi = ref.width() - 1;
32880           if (nxi>=ref.width()) nxi = ref.width() - 1;
32881           if (yi<0) yi = 0;
32882           if (nyi<0) nyi = 0;
32883           if (yi>=ref.height()) yi = ref.height() - 1;
32884           if (nyi>=ref.height()) nyi = ref.height() - 1;
32885           if (zi<0) zi = 0;
32886           if (nzi<0) nzi = 0;
32887           if (zi>=ref.depth()) zi = ref.depth() - 1;
32888           if (nzi>=ref.depth()) nzi = ref.depth() - 1;
32889           I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1);
32890           I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0);
32891           I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
32892           I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1);
32893           I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0);
32894           I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2);
32895           I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1);
32896           I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0);
32897           I(1,0,1,1) = (float)ref(nxi,yi,nzi,1);  I(1,0,1,2) = (float)ref(nxi,yi,nzi,2);
32898           I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1);
32899           I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0);
32900           I(0,1,1,1) = (float)ref(xi,nyi,nzi,1);  I(0,1,1,2) = (float)ref(xi,nyi,nzi,2);
32901           _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
32902           _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
32903         }
32904         return (float)pI->_linear_atXYZ(dx,dy,dz,c);
32905       }
32906     };
32907 
32908     struct _functor4d_streamline_expr {
32909       _cimg_math_parser *mp;
32910       ~_functor4d_streamline_expr() { mp->end(); delete mp; }
32911       _functor4d_streamline_expr(const char *const expr):mp(0) {
32912         mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0);
32913       }
32914       float operator()(const float x, const float y, const float z, const unsigned int c) const {
32915         return (float)(*mp)(x,y,z,c);
32916       }
32917     };
32918 
32919     //! Return a shared-memory image referencing a range of pixels of the image instance.
32920     /**
32921        \param x0 X-coordinate of the starting pixel.
32922        \param x1 X-coordinate of the ending pixel.
32923        \param y0 Y-coordinate.
32924        \param z0 Z-coordinate.
32925        \param c0 C-coordinate.
32926      **/
32927     CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
32928                               const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
32929       const unsigned int
32930         beg = (unsigned int)offset(x0,y0,z0,c0),
32931         end = (unsigned int)offset(x1,y0,z0,c0);
32932       if (beg>end || beg>=size() || end>=size())
32933         throw CImgArgumentException(_cimg_instance
32934                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
32935                                     cimg_instance,
32936                                     x0,x1,y0,z0,c0);
32937 
32938       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
32939     }
32940 
32941     //! Return a shared-memory image referencing a range of pixels of the image instance \const.
32942     const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
32943                                     const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
32944       const unsigned int
32945         beg = (unsigned int)offset(x0,y0,z0,c0),
32946         end = (unsigned int)offset(x1,y0,z0,c0);
32947       if (beg>end || beg>=size() || end>=size())
32948         throw CImgArgumentException(_cimg_instance
32949                                     "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
32950                                     cimg_instance,
32951                                     x0,x1,y0,z0,c0);
32952 
32953       return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
32954     }
32955 
32956     //! Return a shared-memory image referencing a range of rows of the image instance.
32957     /**
32958        \param y0 Y-coordinate of the starting row.
32959        \param y1 Y-coordinate of the ending row.
32960        \param z0 Z-coordinate.
32961        \param c0 C-coordinate.
32962     **/
32963     CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
32964                              const unsigned int z0=0, const unsigned int c0=0) {
32965       const unsigned int
32966         beg = (unsigned int)offset(0,y0,z0,c0),
32967         end = (unsigned int)offset(0,y1,z0,c0);
32968       if (beg>end || beg>=size() || end>=size())
32969         throw CImgArgumentException(_cimg_instance
32970                                     "get_shared_rows(): Invalid request of a shared-memory subset "
32971                                     "(0->%u,%u->%u,%u,%u).",
32972                                     cimg_instance,
32973                                     _width - 1,y0,y1,z0,c0);
32974 
32975       return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
32976     }
32977 
32978     //! Return a shared-memory image referencing a range of rows of the image instance \const.
32979     const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
32980                                    const unsigned int z0=0, const unsigned int c0=0) const {
32981       const unsigned int
32982         beg = (unsigned int)offset(0,y0,z0,c0),
32983         end = (unsigned int)offset(0,y1,z0,c0);
32984       if (beg>end || beg>=size() || end>=size())
32985         throw CImgArgumentException(_cimg_instance
32986                                     "get_shared_rows(): Invalid request of a shared-memory subset "
32987                                     "(0->%u,%u->%u,%u,%u).",
32988                                     cimg_instance,
32989                                     _width - 1,y0,y1,z0,c0);
32990 
32991       return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
32992     }
32993 
32994     //! Return a shared-memory image referencing one row of the image instance.
32995     /**
32996        \param y0 Y-coordinate.
32997        \param z0 Z-coordinate.
32998        \param c0 C-coordinate.
32999     **/
33000     CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
33001       return get_shared_rows(y0,y0,z0,c0);
33002     }
33003 
33004     //! Return a shared-memory image referencing one row of the image instance \const.
33005     const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
33006       return get_shared_rows(y0,y0,z0,c0);
33007     }
33008 
33009     //! Return a shared memory image referencing a range of slices of the image instance.
33010     /**
33011        \param z0 Z-coordinate of the starting slice.
33012        \param z1 Z-coordinate of the ending slice.
33013        \param c0 C-coordinate.
33014     **/
33015     CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
33016       const unsigned int
33017         beg = (unsigned int)offset(0,0,z0,c0),
33018         end = (unsigned int)offset(0,0,z1,c0);
33019       if (beg>end || beg>=size() || end>=size())
33020         throw CImgArgumentException(_cimg_instance
33021                                     "get_shared_slices(): Invalid request of a shared-memory subset "
33022                                     "(0->%u,0->%u,%u->%u,%u).",
33023                                     cimg_instance,
33024                                     _width - 1,_height - 1,z0,z1,c0);
33025 
33026       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
33027     }
33028 
33029     //! Return a shared memory image referencing a range of slices of the image instance \const.
33030     const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
33031       const unsigned int
33032         beg = (unsigned int)offset(0,0,z0,c0),
33033         end = (unsigned int)offset(0,0,z1,c0);
33034       if (beg>end || beg>=size() || end>=size())
33035         throw CImgArgumentException(_cimg_instance
33036                                     "get_shared_slices(): Invalid request of a shared-memory subset "
33037                                     "(0->%u,0->%u,%u->%u,%u).",
33038                                     cimg_instance,
33039                                     _width - 1,_height - 1,z0,z1,c0);
33040 
33041       return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
33042     }
33043 
33044     //! Return a shared-memory image referencing one slice of the image instance.
33045     /**
33046        \param z0 Z-coordinate.
33047        \param c0 C-coordinate.
33048     **/
33049     CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
33050       return get_shared_slices(z0,z0,c0);
33051     }
33052 
33053     //! Return a shared-memory image referencing one slice of the image instance \const.
33054     const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
33055       return get_shared_slices(z0,z0,c0);
33056     }
33057 
33058     //! Return a shared-memory image referencing a range of channels of the image instance.
33059     /**
33060        \param c0 C-coordinate of the starting channel.
33061        \param c1 C-coordinate of the ending channel.
33062     **/
33063     CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
33064       const unsigned int
33065         beg = (unsigned int)offset(0,0,0,c0),
33066         end = (unsigned int)offset(0,0,0,c1);
33067       if (beg>end || beg>=size() || end>=size())
33068         throw CImgArgumentException(_cimg_instance
33069                                     "get_shared_channels(): Invalid request of a shared-memory subset "
33070                                     "(0->%u,0->%u,0->%u,%u->%u).",
33071                                     cimg_instance,
33072                                     _width - 1,_height - 1,_depth - 1,c0,c1);
33073 
33074       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
33075     }
33076 
33077     //! Return a shared-memory image referencing a range of channels of the image instance \const.
33078     const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
33079       const unsigned int
33080         beg = (unsigned int)offset(0,0,0,c0),
33081         end = (unsigned int)offset(0,0,0,c1);
33082       if (beg>end || beg>=size() || end>=size())
33083         throw CImgArgumentException(_cimg_instance
33084                                     "get_shared_channels(): Invalid request of a shared-memory subset "
33085                                     "(0->%u,0->%u,0->%u,%u->%u).",
33086                                     cimg_instance,
33087                                     _width - 1,_height - 1,_depth - 1,c0,c1);
33088 
33089       return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
33090     }
33091 
33092     //! Return a shared-memory image referencing one channel of the image instance.
33093     /**
33094        \param c0 C-coordinate.
33095     **/
33096     CImg<T> get_shared_channel(const unsigned int c0) {
33097       return get_shared_channels(c0,c0);
33098     }
33099 
33100     //! Return a shared-memory image referencing one channel of the image instance \const.
33101     const CImg<T> get_shared_channel(const unsigned int c0) const {
33102       return get_shared_channels(c0,c0);
33103     }
33104 
33105     //! Return a shared-memory version of the image instance.
33106     CImg<T> get_shared() {
33107       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
33108     }
33109 
33110     //! Return a shared-memory version of the image instance \const.
33111     const CImg<T> get_shared() const {
33112       return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
33113     }
33114 
33115     //! Split image into a list along specified axis.
33116     /**
33117        \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
33118        \param nb Number of splitted parts.
33119        \note
33120        - If \c nb==0, instance image is splitted into blocs of egal values along the specified axis.
33121        - If \c nb<=0, instance image is splitted into blocs of -\c nb pixel wide.
33122        - If \c nb>0, instance image is splitted into \c nb blocs.
33123     **/
33124     CImgList<T> get_split(const char axis, const int nb=-1) const {
33125       CImgList<T> res;
33126       if (is_empty()) return res;
33127       const char _axis = cimg::lowercase(axis);
33128 
33129       if (nb<0) { // Split by bloc size.
33130         const unsigned int dp = (unsigned int)(nb?-nb:1);
33131         switch (_axis) {
33132         case 'x': {
33133           if (_width>dp) {
33134             res.assign(_width/dp + (_width%dp?1:0),1,1);
33135             const unsigned int pe = _width - dp;
33136             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _height*_depth*_spectrum>=128))
33137             for (unsigned int p = 0; p<pe; p+=dp)
33138               get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
33139             get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
33140           } else res.assign(*this);
33141         } break;
33142         case 'y': {
33143           if (_height>dp) {
33144             res.assign(_height/dp + (_height%dp?1:0),1,1);
33145             const unsigned int pe = _height - dp;
33146             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _width*_depth*_spectrum>=128))
33147             for (unsigned int p = 0; p<pe; p+=dp)
33148               get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
33149             get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
33150           } else res.assign(*this);
33151         } break;
33152         case 'z': {
33153           if (_depth>dp) {
33154             res.assign(_depth/dp + (_depth%dp?1:0),1,1);
33155             const unsigned int pe = _depth - dp;
33156             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _width*_height*_spectrum>=128))
33157             for (unsigned int p = 0; p<pe; p+=dp)
33158               get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]);
33159             get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
33160           } else res.assign(*this);
33161         } break;
33162         case 'c' : {
33163           if (_spectrum>dp) {
33164             res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1);
33165             const unsigned int pe = _spectrum - dp;
33166             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=128 && _width*_height*_depth>=128))
33167             for (unsigned int p = 0; p<pe; p+=dp)
33168               get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]);
33169             get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
33170           } else res.assign(*this);
33171         }
33172         }
33173       } else if (nb>0) { // Split by number of (non-homogeneous) blocs.
33174         const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
33175         if ((unsigned int)nb>siz)
33176           throw CImgArgumentException(_cimg_instance
33177                                       "get_split(): Instance cannot be split along %c-axis into %u blocs.",
33178                                       cimg_instance,
33179                                       axis,nb);
33180         if (nb==1) res.assign(*this);
33181         else {
33182           int err = (int)siz;
33183           unsigned int _p = 0;
33184           switch (_axis) {
33185           case 'x' : {
33186             cimg_forX(*this,p) if ((err-=nb)<=0) {
33187               get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res);
33188               err+=(int)siz;
33189               _p = p + 1U;
33190             }
33191           } break;
33192           case 'y' : {
33193             cimg_forY(*this,p) if ((err-=nb)<=0) {
33194               get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res);
33195               err+=(int)siz;
33196               _p = p + 1U;
33197             }
33198           } break;
33199           case 'z' : {
33200             cimg_forZ(*this,p) if ((err-=nb)<=0) {
33201               get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res);
33202               err+=(int)siz;
33203               _p = p + 1U;
33204             }
33205           } break;
33206           case 'c' : {
33207             cimg_forC(*this,p) if ((err-=nb)<=0) {
33208               get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res);
33209               err+=(int)siz;
33210               _p = p + 1U;
33211             }
33212           }
33213           }
33214         }
33215       } else { // Split by egal values according to specified axis.
33216         T current = *_data;
33217         switch (_axis) {
33218         case 'x' : {
33219           int i0 = 0;
33220           cimg_forX(*this,i)
33221             if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); }
33222           get_columns(i0,width() - 1).move_to(res);
33223         } break;
33224         case 'y' : {
33225           int i0 = 0;
33226           cimg_forY(*this,i)
33227             if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); }
33228           get_rows(i0,height() - 1).move_to(res);
33229         } break;
33230         case 'z' : {
33231           int i0 = 0;
33232           cimg_forZ(*this,i)
33233             if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); }
33234           get_slices(i0,depth() - 1).move_to(res);
33235         } break;
33236         case 'c' : {
33237           int i0 = 0;
33238           cimg_forC(*this,i)
33239             if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); }
33240           get_channels(i0,spectrum() - 1).move_to(res);
33241         } break;
33242         default : {
33243           longT i0 = 0;
33244           cimg_foroff(*this,i)
33245             if ((*this)[i]!=current) {
33246               CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
33247               i0 = (longT)i; current = (*this)[i];
33248             }
33249           CImg<T>(_data + i0,1,(unsigned int)(size() - i0)).move_to(res);
33250         }
33251         }
33252       }
33253       return res;
33254     }
33255 
33256     //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis.
33257     /**
33258        \param values Splitting value sequence.
33259        \param axis Axis along which the splitting is performed. Can be '0' to ignore axis.
33260        \param keep_values Tells if the splitting sequence must be kept in the splitted blocs.
33261      **/
33262     template<typename t>
33263     CImgList<T> get_split(const CImg<t>& values, const char axis=0, const bool keep_values=true) const {
33264       CImgList<T> res;
33265       if (is_empty()) return res;
33266       const ulongT vsiz = values.size();
33267       const char _axis = cimg::lowercase(axis);
33268       if (!vsiz) return CImgList<T>(*this);
33269       if (vsiz==1) { // Split according to a single value.
33270         const T value = (T)*values;
33271         switch (_axis) {
33272         case 'x' : {
33273           unsigned int i0 = 0, i = 0;
33274           do {
33275             while (i<_width && (*this)(i)==value) ++i;
33276             if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; }
33277             while (i<_width && (*this)(i)!=value) ++i;
33278             if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; }
33279           } while (i<_width);
33280         } break;
33281         case 'y' : {
33282           unsigned int i0 = 0, i = 0;
33283           do {
33284             while (i<_height && (*this)(0,i)==value) ++i;
33285             if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; }
33286             while (i<_height && (*this)(0,i)!=value) ++i;
33287             if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; }
33288           } while (i<_height);
33289         } break;
33290         case 'z' : {
33291           unsigned int i0 = 0, i = 0;
33292           do {
33293             while (i<_depth && (*this)(0,0,i)==value) ++i;
33294             if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; }
33295             while (i<_depth && (*this)(0,0,i)!=value) ++i;
33296             if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; }
33297           } while (i<_depth);
33298         } break;
33299         case 'c' : {
33300           unsigned int i0 = 0, i = 0;
33301           do {
33302             while (i<_spectrum && (*this)(0,0,0,i)==value) ++i;
33303             if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; }
33304             while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i;
33305             if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; }
33306           } while (i<_spectrum);
33307         } break;
33308         default : {
33309           const ulongT siz = size();
33310           ulongT i0 = 0, i = 0;
33311           do {
33312             while (i<siz && (*this)[i]==value) ++i;
33313             if (i>i0) { if (keep_values) CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
33314             while (i<siz && (*this)[i]!=value) ++i;
33315             if (i>i0) { CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
33316           } while (i<siz);
33317         }
33318         }
33319       } else { // Split according to multiple values.
33320         ulongT j = 0;
33321         switch (_axis) {
33322         case 'x' : {
33323           unsigned int i0 = 0, i1 = 0, i = 0;
33324           do {
33325             if ((*this)(i)==*values) {
33326               i1 = i; j = 0;
33327               while (i<_width && (*this)(i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33328               i-=j;
33329               if (i>i1) {
33330                 if (i1>i0) get_columns(i0,i1 - 1).move_to(res);
33331                 if (keep_values) get_columns(i1,i - 1).move_to(res);
33332                 i0 = i;
33333               } else ++i;
33334             } else ++i;
33335           } while (i<_width);
33336           if (i0<_width) get_columns(i0,width() - 1).move_to(res);
33337         } break;
33338         case 'y' : {
33339           unsigned int i0 = 0, i1 = 0, i = 0;
33340           do {
33341             if ((*this)(0,i)==*values) {
33342               i1 = i; j = 0;
33343               while (i<_height && (*this)(0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33344               i-=j;
33345               if (i>i1) {
33346                 if (i1>i0) get_rows(i0,i1 - 1).move_to(res);
33347                 if (keep_values) get_rows(i1,i - 1).move_to(res);
33348                 i0 = i;
33349               } else ++i;
33350             } else ++i;
33351           } while (i<_height);
33352           if (i0<_height) get_rows(i0,height() - 1).move_to(res);
33353         } break;
33354         case 'z' : {
33355           unsigned int i0 = 0, i1 = 0, i = 0;
33356           do {
33357             if ((*this)(0,0,i)==*values) {
33358               i1 = i; j = 0;
33359               while (i<_depth && (*this)(0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33360               i-=j;
33361               if (i>i1) {
33362                 if (i1>i0) get_slices(i0,i1 - 1).move_to(res);
33363                 if (keep_values) get_slices(i1,i - 1).move_to(res);
33364                 i0 = i;
33365               } else ++i;
33366             } else ++i;
33367           } while (i<_depth);
33368           if (i0<_depth) get_slices(i0,depth() - 1).move_to(res);
33369         } break;
33370         case 'c' : {
33371           unsigned int i0 = 0, i1 = 0, i = 0;
33372           do {
33373             if ((*this)(0,0,0,i)==*values) {
33374               i1 = i; j = 0;
33375               while (i<_spectrum && (*this)(0,0,0,i)==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33376               i-=j;
33377               if (i>i1) {
33378                 if (i1>i0) get_channels(i0,i1 - 1).move_to(res);
33379                 if (keep_values) get_channels(i1,i - 1).move_to(res);
33380                 i0 = i;
33381               } else ++i;
33382             } else ++i;
33383           } while (i<_spectrum);
33384           if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res);
33385         } break;
33386         default : {
33387           ulongT i0 = 0, i1 = 0, i = 0;
33388           const ulongT siz = size();
33389           do {
33390             if ((*this)[i]==*values) {
33391               i1 = i; j = 0;
33392               while (i<siz && (*this)[i]==values[j]) { ++i; if (++j>=vsiz) j = 0; }
33393               i-=j;
33394               if (i>i1) {
33395                 if (i1>i0) CImg<T>(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res);
33396                 if (keep_values) CImg<T>(_data + i1,1,(unsigned int)(i - i1)).move_to(res);
33397                 i0 = i;
33398               } else ++i;
33399             } else ++i;
33400           } while (i<siz);
33401           if (i0<siz) CImg<T>(_data + i0,1,(unsigned int)(siz - i0)).move_to(res);
33402         } break;
33403         }
33404       }
33405       return res;
33406     }
33407 
33408     //! Append two images along specified axis.
33409     /**
33410        \param img Image to append with instance image.
33411        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
33412        \param align Append alignment in \c [0,1].
33413     **/
33414     template<typename t>
33415     CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
33416       if (is_empty()) return assign(img,false);
33417       if (!img) return *this;
33418       return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
33419     }
33420 
33421     //! Append two images along specified axis \specialization.
33422     CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
33423       if (is_empty()) return assign(img,false);
33424       if (!img) return *this;
33425       return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
33426     }
33427 
33428     //! Append two images along specified axis \const.
33429     template<typename t>
33430     CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
33431       if (is_empty()) return +img;
33432       if (!img) return +*this;
33433       return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
33434     }
33435 
33436     //! Append two images along specified axis \specialization.
33437     CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
33438       if (is_empty()) return +img;
33439       if (!img) return +*this;
33440       return CImgList<T>(*this,img,true).get_append(axis,align);
33441     }
33442 
33443     //@}
33444     //---------------------------------------
33445     //
33446     //! \name Filtering / Transforms
33447     //@{
33448     //---------------------------------------
33449 
33450     //! Correlate image by a kernel.
33451     /**
33452        \param kernel = the correlation kernel.
33453        \param boundary_conditions boundary conditions can be (false=dirichlet, true=neumann)
33454        \param is_normalized = enable local normalization.
33455        \note
33456        - The correlation of the image instance \p *this by the kernel \p kernel is defined to be:
33457        res(x,y,z) = sum_{i,j,k} (*this)(x + i,y + j,z + k)*kernel(i,j,k).
33458     **/
33459     template<typename t>
33460     CImg<T>& correlate(const CImg<t>& kernel, const bool boundary_conditions=true,
33461                        const bool is_normalized=false) {
33462       if (is_empty() || !kernel) return *this;
33463       return get_correlate(kernel,boundary_conditions,is_normalized).move_to(*this);
33464     }
33465 
33466     template<typename t>
33467     CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const bool boundary_conditions=true,
33468                                       const bool is_normalized=false) const {
33469       return _correlate(kernel,boundary_conditions,is_normalized,false);
33470     }
33471 
33472     //! Correlate image by a kernel \newinstance.
33473     template<typename t>
33474     CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const bool boundary_conditions,
33475                                    const bool is_normalized, const bool is_convolution) const {
33476       if (is_empty() || !kernel) return *this;
33477       typedef _cimg_Ttfloat Ttfloat;
33478       CImg<Ttfloat> res;
33479       const ulongT
33480         res_whd = (ulongT)_width*_height*_depth,
33481         res_size = res_whd*std::max(_spectrum,kernel._spectrum);
33482       const bool
33483         is_inner_parallel = _width*_height*_depth>=32768,
33484         is_outer_parallel = res_size>=32768;
33485       _cimg_abort_init_omp;
33486       cimg_abort_init;
33487 
33488       if (kernel._width==kernel._height &&
33489           ((kernel._depth==1 && kernel._width<=6) || (kernel._depth==kernel._width && kernel._width<=3))) {
33490 
33491         // Special optimization done for 2x2, 3x3, 4x4, 5x5, 6x6, 2x2x2 and 3x3x3 kernel.
33492         if (!boundary_conditions && res_whd<=3000*3000) { // Dirichlet boundaries
33493           // For relatively small images, adding a zero border then use optimized NxN convolution loops is faster.
33494           res = (kernel._depth==1?get_crop(-1,-1,_width,_height):get_crop(-1,-1,-1,_width,_height,_depth)).
33495             _correlate(kernel,true,is_normalized,is_convolution);
33496           if (kernel._depth==1) res.crop(1,1,res._width - 2,res._height - 2);
33497           else res.crop(1,1,1,res._width - 2,res._height - 2,res._depth - 2);
33498 
33499         } else { // Neumann boundaries
33500           res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
33501           cimg::unused(is_inner_parallel,is_outer_parallel);
33502           CImg<t> _kernel;
33503           if (is_convolution) { // Add empty column/row/slice to shift kernel center in case of convolution
33504             const int dw = !(kernel.width()%2), dh = !(kernel.height()%2), dd = !(kernel.depth()%2);
33505             if (dw || dh || dd)
33506               kernel.get_resize(kernel.width() + dw,kernel.height() + dh,kernel.depth() + dd,-100,0,0).
33507                 move_to(_kernel);
33508           }
33509           if (!_kernel) _kernel = kernel.get_shared();
33510 
33511           switch (_kernel._depth) {
33512           case 3 : {
33513             cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33514               cimg_forC(res,c) {
33515               cimg_abort_test;
33516               const CImg<T> img = get_shared_channel(c%_spectrum);
33517               const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33518               CImg<T> I(27);
33519               Ttfloat *ptrd = res.data(0,0,0,c);
33520               if (is_normalized) {
33521                 const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33522                 cimg_for3x3x3(img,x,y,z,0,I,T) {
33523                   const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] +
33524                                        I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
33525                                        I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] +
33526                                        I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
33527                                        I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
33528                                        I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
33529                                        I[18]*I[18] + I[19]*I[19] + I[20]*I[20] +
33530                                        I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
33531                                        I[24]*I[24] + I[25]*I[25] + I[26]*I[26]);
33532                   *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] +
33533                                            I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] +
33534                                            I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] +
33535                                            I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33536                                            I[12]*K[12] + I[13]*K[13] + I[14]*K[14] +
33537                                            I[15]*K[15] + I[16]*K[16] + I[17]*K[17] +
33538                                            I[18]*K[18] + I[19]*K[19] + I[20]*K[20] +
33539                                            I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33540                                            I[24]*K[24] + I[25]*K[25] + I[26]*K[26])/std::sqrt(N):0);
33541                 }
33542               } else cimg_for3x3x3(img,x,y,z,0,I,T)
33543                        *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] +
33544                                              I[ 3]*K[ 3] + I[ 4]*K[ 4] + I[ 5]*K[ 5] +
33545                                              I[ 6]*K[ 6] + I[ 7]*K[ 7] + I[ 8]*K[ 8] +
33546                                              I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33547                                              I[12]*K[12] + I[13]*K[13] + I[14]*K[14] +
33548                                              I[15]*K[15] + I[16]*K[16] + I[17]*K[17] +
33549                                              I[18]*K[18] + I[19]*K[19] + I[20]*K[20] +
33550                                              I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33551                                              I[24]*K[24] + I[25]*K[25] + I[26]*K[26]);
33552             }
33553           } break;
33554           case 2 : {
33555             cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33556               cimg_forC(res,c) {
33557               cimg_abort_test;
33558               const CImg<T> img = get_shared_channel(c%_spectrum);
33559               const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33560               CImg<T> I(8);
33561               Ttfloat *ptrd = res.data(0,0,0,c);
33562               if (is_normalized) {
33563                 const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33564                 cimg_for2x2x2(img,x,y,z,0,I,T) {
33565                   const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
33566                                        I[2]*I[2] + I[3]*I[3] +
33567                                        I[4]*I[4] + I[5]*I[5] +
33568                                        I[6]*I[6] + I[7]*I[7]);
33569                   *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] +
33570                                            I[2]*K[2] + I[3]*K[3] +
33571                                            I[4]*K[4] + I[5]*K[5] +
33572                                            I[6]*K[6] + I[7]*K[7])/std::sqrt(N):0);
33573                 }
33574               } else cimg_for2x2x2(img,x,y,z,0,I,T)
33575                        *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] +
33576                                              I[2]*K[2] + I[3]*K[3] +
33577                                              I[4]*K[4] + I[5]*K[5] +
33578                                              I[6]*K[6] + I[7]*K[7]);
33579             }
33580           } break;
33581           default :
33582           case 1 :
33583             switch (_kernel._width) {
33584             case 6 : {
33585               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33586                 cimg_forC(res,c) {
33587                 cimg_abort_test;
33588                 const CImg<T> img = get_shared_channel(c%_spectrum);
33589                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33590                 CImg<T> I(36);
33591                 Ttfloat *ptrd = res.data(0,0,0,c);
33592                 if (is_normalized) {
33593                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33594                   cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T) {
33595                     const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
33596                                          I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
33597                                          I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
33598                                          I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
33599                                          I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24] +
33600                                          I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] +
33601                                          I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] +
33602                                          I[35]*I[35]);
33603                     *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33604                                              I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33605                                              I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33606                                              I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
33607                                              I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
33608                                              I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33609                                              I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] +
33610                                              I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] +
33611                                              I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35])/
33612                                           std::sqrt(N):0);
33613                   }
33614                 } else cimg_forZ(img,z) cimg_for6x6(img,x,y,z,0,I,T)
33615                          *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33616                                                I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33617                                                I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33618                                                I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
33619                                                I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
33620                                                I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33621                                                I[24]*K[24] + I[25]*K[25] + I[26]*K[26] + I[27]*K[27] +
33622                                                I[28]*K[28] + I[29]*K[29] + I[30]*K[30] + I[31]*K[31] +
33623                                                I[32]*K[32] + I[33]*K[33] + I[34]*K[34] + I[35]*K[35]);
33624               }
33625             } break;
33626             case 5 : {
33627               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33628                 cimg_forC(res,c) {
33629                 cimg_abort_test;
33630                 const CImg<T> img = get_shared_channel(c%_spectrum);
33631                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33632                 CImg<T> I(25);
33633                 Ttfloat *ptrd = res.data(0,0,0,c);
33634                 if (is_normalized) {
33635                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33636                   cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T) {
33637                     const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
33638                                          I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
33639                                          I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
33640                                          I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
33641                                          I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]);
33642                     *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33643                                              I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33644                                              I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33645                                              I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
33646                                              I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
33647                                              I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33648                                              I[24]*K[24])/std::sqrt(N):0);
33649                   }
33650                 } else cimg_forZ(img,z) cimg_for5x5(img,x,y,z,0,I,T)
33651                          *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33652                                                I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33653                                                I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33654                                                I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15] +
33655                                                I[16]*K[16] + I[17]*K[17] + I[18]*K[18] + I[19]*K[19] +
33656                                                I[20]*K[20] + I[21]*K[21] + I[22]*K[22] + I[23]*K[23] +
33657                                                I[24]*K[24]);
33658               }
33659             } break;
33660             case 4 : {
33661               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33662                 cimg_forC(res,c) {
33663                 cimg_abort_test;
33664                 const CImg<T> img = get_shared_channel(c%_spectrum);
33665                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33666                 CImg<T> I(16);
33667                 Ttfloat *ptrd = res.data(0,0,0,c);
33668                 if (is_normalized) {
33669                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33670                   cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T) {
33671                     const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] +
33672                                          I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] +
33673                                          I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
33674                                          I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]);
33675                     *(ptrd++) = (Ttfloat)(N?(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33676                                              I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33677                                              I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33678                                              I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15])/
33679                                           std::sqrt(N):0);
33680                   }
33681                 } else cimg_forZ(img,z) cimg_for4x4(img,x,y,z,0,I,T)
33682                          *(ptrd++) = (Ttfloat)(I[ 0]*K[ 0] + I[ 1]*K[ 1] + I[ 2]*K[ 2] + I[ 3]*K[ 3] +
33683                                                I[ 4]*K[ 4] + I[ 5]*K[ 5] + I[ 6]*K[ 6] + I[ 7]*K[ 7] +
33684                                                I[ 8]*K[ 8] + I[ 9]*K[ 9] + I[10]*K[10] + I[11]*K[11] +
33685                                                I[12]*K[12] + I[13]*K[13] + I[14]*K[14] + I[15]*K[15]);
33686               }
33687             } break;
33688             case 3 : {
33689               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33690                 cimg_forC(res,c) {
33691                 cimg_abort_test;
33692                 const CImg<T> img = get_shared_channel(c%_spectrum);
33693                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33694                 CImg<T> I(9);
33695                 Ttfloat *ptrd = res.data(0,0,0,c);
33696                 if (is_normalized) {
33697                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33698                   cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T) {
33699                     const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] +
33700                                          I[3]*I[3] + I[4]*I[4] + I[5]*I[5] +
33701                                          I[6]*I[6] + I[7]*I[7] + I[8]*I[8]);
33702                     *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] +
33703                                              I[3]*K[3] + I[4]*K[4] + I[5]*K[5] +
33704                                              I[6]*K[6] + I[7]*K[7] + I[8]*K[8])/std::sqrt(N):0);
33705                   }
33706                 } else cimg_forZ(img,z) cimg_for3x3(img,x,y,z,0,I,T)
33707                          *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] + I[2]*K[2] +
33708                                                I[3]*K[3] + I[4]*K[4] + I[5]*K[5] +
33709                                                I[6]*K[6] + I[7]*K[7] + I[8]*K[8]);
33710               }
33711             } break;
33712             case 2 : {
33713               cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
33714                 cimg_forC(res,c) {
33715                 cimg_abort_test;
33716                 const CImg<T> img = get_shared_channel(c%_spectrum);
33717                 const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33718                 CImg<T> I(4);
33719                 Ttfloat *ptrd = res.data(0,0,0,c);
33720                 if (is_normalized) {
33721                   const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33722                   cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T) {
33723                     const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
33724                                          I[2]*I[2] + I[3]*I[3]);
33725                     *(ptrd++) = (Ttfloat)(N?(I[0]*K[0] + I[1]*K[1] +
33726                                              I[2]*K[2] + I[3]*K[3])/std::sqrt(N):0);
33727                   }
33728                 } else cimg_forZ(img,z) cimg_for2x2(img,x,y,z,0,I,T)
33729                          *(ptrd++) = (Ttfloat)(I[0]*K[0] + I[1]*K[1] +
33730                                                I[2]*K[2] + I[3]*K[3]);
33731               }
33732             } break;
33733             case 1 :
33734               if (is_normalized) res.fill(1);
33735               else cimg_forC(res,c) {
33736                   cimg_abort_test;
33737                   const CImg<T> img = get_shared_channel(c%_spectrum);
33738                   const CImg<t> K = _kernel.get_shared_channel(c%kernel._spectrum);
33739                   res.get_shared_channel(c).assign(img)*=K[0];
33740                 }
33741               break;
33742             }
33743           }
33744         }
33745       }
33746 
33747       if (!res) { // Generic version for other kernels and boundary conditions.
33748         res.assign(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
33749         int
33750           mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
33751           mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1;
33752         if (is_convolution) cimg::swap(mx1,mx2,my1,my2,mz1,mz2); // Shift kernel center in case of convolution
33753         const int
33754           mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
33755         cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
33756         cimg_forC(res,c) _cimg_abort_try_omp {
33757           cimg_abort_test;
33758           const CImg<T> img = get_shared_channel(c%_spectrum);
33759           const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
33760           if (is_normalized) { // Normalized correlation.
33761             const Ttfloat _M = (Ttfloat)K.magnitude(2), M = _M*_M;
33762             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
33763             for (int z = mz1; z<mze; ++z)
33764               for (int y = my1; y<mye; ++y)
33765                 for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
33766                   cimg_abort_test2;
33767                   Ttfloat val = 0, N = 0;
33768                   for (int zm = -mz1; zm<=mz2; ++zm)
33769                     for (int ym = -my1; ym<=my2; ++ym)
33770                       for (int xm = -mx1; xm<=mx2; ++xm) {
33771                         const Ttfloat _val = (Ttfloat)img(x + xm,y + ym,z + zm);
33772                         val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
33773                         N+=_val*_val;
33774                       }
33775                   N*=M;
33776                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
33777                 } _cimg_abort_catch_omp2
33778             if (boundary_conditions)
33779               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
33780               cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
33781                 cimg_abort_test2;
33782                 for (int x = 0; x<width();
33783                      (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
33784                   Ttfloat val = 0, N = 0;
33785                   for (int zm = -mz1; zm<=mz2; ++zm)
33786                     for (int ym = -my1; ym<=my2; ++ym)
33787                       for (int xm = -mx1; xm<=mx2; ++xm) {
33788                         const Ttfloat _val = (Ttfloat)img._atXYZ(x + xm,y + ym,z + zm);
33789                         val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
33790                         N+=_val*_val;
33791                       }
33792                   N*=M;
33793                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
33794                 }
33795               } _cimg_abort_catch_omp2
33796             else
33797               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
33798               cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
33799                 cimg_abort_test2;
33800                 for (int x = 0; x<width();
33801                      (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
33802                   Ttfloat val = 0, N = 0;
33803                   for (int zm = -mz1; zm<=mz2; ++zm)
33804                     for (int ym = -my1; ym<=my2; ++ym)
33805                       for (int xm = -mx1; xm<=mx2; ++xm) {
33806                         const Ttfloat _val = (Ttfloat)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
33807                         val+=_val*K(mx1 + xm,my1 + ym,mz1 + zm);
33808                         N+=_val*_val;
33809                       }
33810                   N*=M;
33811                   res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
33812                 }
33813               } _cimg_abort_catch_omp2
33814           } else { // Classical correlation.
33815             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
33816               for (int z = mz1; z<mze; ++z)
33817               for (int y = my1; y<mye; ++y)
33818                 for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
33819                   cimg_abort_test2;
33820                   Ttfloat val = 0;
33821                   for (int zm = -mz1; zm<=mz2; ++zm)
33822                     for (int ym = -my1; ym<=my2; ++ym)
33823                       for (int xm = -mx1; xm<=mx2; ++xm)
33824                         val+=img(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm);
33825                   res(x,y,z,c) = (Ttfloat)val;
33826                 } _cimg_abort_catch_omp2
33827             if (boundary_conditions)
33828               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
33829               cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
33830                 cimg_abort_test2;
33831                 for (int x = 0; x<width();
33832                      (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
33833                   Ttfloat val = 0;
33834                   for (int zm = -mz1; zm<=mz2; ++zm)
33835                     for (int ym = -my1; ym<=my2; ++ym)
33836                       for (int xm = -mx1; xm<=mx2; ++xm)
33837                         val+=img._atXYZ(x + xm,y + ym,z + zm)*K(mx1 + xm,my1 + ym,mz1 + zm);
33838                   res(x,y,z,c) = (Ttfloat)val;
33839                 }
33840               } _cimg_abort_catch_omp2
33841             else
33842               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
33843               cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
33844                 cimg_abort_test2;
33845                 for (int x = 0; x<width();
33846                      (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
33847                   Ttfloat val = 0;
33848                   for (int zm = -mz1; zm<=mz2; ++zm)
33849                     for (int ym = -my1; ym<=my2; ++ym)
33850                       for (int xm = -mx1; xm<=mx2; ++xm)
33851                         val+=img.atXYZ(x + xm,y + ym,z + zm,0,(T)0)*K(mx1 + xm,my1 + ym,mz1 + zm);
33852                   res(x,y,z,c) = (Ttfloat)val;
33853                 }
33854               } _cimg_abort_catch_omp2
33855           }
33856         } _cimg_abort_catch_omp
33857       }
33858       cimg_abort_test;
33859       return res;
33860     }
33861 
33862     //! Convolve image by a kernel.
33863     /**
33864        \param kernel = the correlation kernel.
33865        \param boundary_conditions boundary conditions can be (false=dirichlet, true=neumann)
33866        \param is_normalized = enable local normalization.
33867        \note
33868        - The result \p res of the convolution of an image \p img by a kernel \p kernel is defined to be:
33869        res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*kernel(i,j,k)
33870     **/
33871     template<typename t>
33872     CImg<T>& convolve(const CImg<t>& kernel, const bool boundary_conditions=true, const bool is_normalized=false) {
33873       if (is_empty() || !kernel) return *this;
33874       return get_convolve(kernel,boundary_conditions,is_normalized).move_to(*this);
33875     }
33876 
33877     //! Convolve image by a kernel \newinstance.
33878     template<typename t>
33879     CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& kernel, const bool boundary_conditions=true,
33880                                      const bool is_normalized=false) const {
33881       return _correlate(CImg<t>(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true).
33882                         get_mirror('x').resize(kernel,-1),boundary_conditions,is_normalized,true);
33883     }
33884 
33885     //! Cumulate image values, optionally along specified axis.
33886     /**
33887        \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account.
33888     **/
33889     CImg<T>& cumulate(const char axis=0) {
33890       switch (cimg::lowercase(axis)) {
33891       case 'x' :
33892         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=512 && _height*_depth*_spectrum>=16))
33893         cimg_forYZC(*this,y,z,c) {
33894           T *ptrd = data(0,y,z,c);
33895           Tlong cumul = (Tlong)0;
33896           cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; }
33897         }
33898         break;
33899       case 'y' : {
33900         const ulongT w = (ulongT)_width;
33901         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_height>=512 && _width*_depth*_spectrum>=16))
33902         cimg_forXZC(*this,x,z,c) {
33903           T *ptrd = data(x,0,z,c);
33904           Tlong cumul = (Tlong)0;
33905           cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; }
33906         }
33907       } break;
33908       case 'z' : {
33909         const ulongT wh = (ulongT)_width*_height;
33910         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_depth>=512 && _width*_depth*_spectrum>=16))
33911         cimg_forXYC(*this,x,y,c) {
33912           T *ptrd = data(x,y,0,c);
33913           Tlong cumul = (Tlong)0;
33914           cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; }
33915         }
33916       } break;
33917       case 'c' : {
33918         const ulongT whd = (ulongT)_width*_height*_depth;
33919         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_spectrum>=512 && _width*_height*_depth>=16))
33920         cimg_forXYZ(*this,x,y,z) {
33921           T *ptrd = data(x,y,z,0);
33922           Tlong cumul = (Tlong)0;
33923           cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; }
33924         }
33925       } break;
33926       default : { // Global cumulation.
33927         Tlong cumul = (Tlong)0;
33928         cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; }
33929       }
33930       }
33931       return *this;
33932     }
33933 
33934     //! Cumulate image values, optionally along specified axis \newinstance.
33935     CImg<Tlong> get_cumulate(const char axis=0) const {
33936       return CImg<Tlong>(*this,false).cumulate(axis);
33937     }
33938 
33939     //! Cumulate image values, along specified axes.
33940     /**
33941        \param axes Cumulation axes, as a C-string.
33942        \note \c axes may contains multiple characters, e.g. \c "xyz"
33943     **/
33944     CImg<T>& cumulate(const char *const axes) {
33945       for (const char *s = axes; *s; ++s) cumulate(*s);
33946       return *this;
33947     }
33948 
33949     //! Cumulate image values, along specified axes \newinstance.
33950     CImg<Tlong> get_cumulate(const char *const axes) const {
33951       return CImg<Tlong>(*this,false).cumulate(axes);
33952     }
33953 
33954     //! Erode image by a structuring element.
33955     /**
33956        \param kernel Structuring element.
33957        \param boundary_conditions Boundary conditions.
33958        \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
33959     **/
33960     template<typename t>
33961     CImg<T>& erode(const CImg<t>& kernel, const bool boundary_conditions=true,
33962                    const bool is_real=false) {
33963       if (is_empty() || !kernel) return *this;
33964       return get_erode(kernel,boundary_conditions,is_real).move_to(*this);
33965     }
33966 
33967     //! Erode image by a structuring element \newinstance.
33968     template<typename t>
33969     CImg<_cimg_Tt> get_erode(const CImg<t>& kernel, const bool boundary_conditions=true,
33970                              const bool is_real=false) const {
33971       if (is_empty() || !kernel) return *this;
33972       if (!is_real && kernel==0) return CImg<T>(width(),height(),depth(),spectrum(),0);
33973       typedef _cimg_Tt Tt;
33974       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
33975       const int
33976         mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
33977         mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1,
33978         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
33979       const bool
33980         is_inner_parallel = _width*_height*_depth>=32768,
33981         is_outer_parallel = res.size()>=32768;
33982       cimg::unused(is_inner_parallel,is_outer_parallel);
33983       _cimg_abort_init_omp;
33984       cimg_abort_init;
33985       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
33986       cimg_forC(res,c) _cimg_abort_try_omp {
33987         cimg_abort_test;
33988         const CImg<T> img = get_shared_channel(c%_spectrum);
33989         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
33990         if (is_real) { // Real erosion
33991           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
33992           for (int z = mz1; z<mze; ++z)
33993             for (int y = my1; y<mye; ++y)
33994               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
33995                 cimg_abort_test2;
33996                 Tt min_val = cimg::type<Tt>::max();
33997                 for (int zm = -mz1; zm<=mz2; ++zm)
33998                   for (int ym = -my1; ym<=my2; ++ym)
33999                     for (int xm = -mx1; xm<=mx2; ++xm) {
34000                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
34001                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval);
34002                       if (cval<min_val) min_val = cval;
34003                     }
34004                 res(x,y,z,c) = min_val;
34005               } _cimg_abort_catch_omp2
34006           if (boundary_conditions)
34007             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34008             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34009               cimg_abort_test2;
34010               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34011                 Tt min_val = cimg::type<Tt>::max();
34012                 for (int zm = -mz1; zm<=mz2; ++zm)
34013                   for (int ym = -my1; ym<=my2; ++ym)
34014                     for (int xm = -mx1; xm<=mx2; ++xm) {
34015                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
34016                       const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval);
34017                       if (cval<min_val) min_val = cval;
34018                     }
34019                 res(x,y,z,c) = min_val;
34020               }
34021             } _cimg_abort_catch_omp2
34022           else
34023             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34024             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34025               cimg_abort_test2;
34026               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34027                 Tt min_val = cimg::type<Tt>::max();
34028                 for (int zm = -mz1; zm<=mz2; ++zm)
34029                   for (int ym = -my1; ym<=my2; ++ym)
34030                     for (int xm = -mx1; xm<=mx2; ++xm) {
34031                       const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
34032                       const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval);
34033                       if (cval<min_val) min_val = cval;
34034                     }
34035                 res(x,y,z,c) = min_val;
34036               }
34037             } _cimg_abort_catch_omp2
34038 
34039         } else { // Binary erosion
34040           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
34041           for (int z = mz1; z<mze; ++z)
34042             for (int y = my1; y<mye; ++y)
34043               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
34044                 cimg_abort_test2;
34045                 Tt min_val = cimg::type<Tt>::max();
34046                 for (int zm = -mz1; zm<=mz2; ++zm)
34047                   for (int ym = -my1; ym<=my2; ++ym)
34048                     for (int xm = -mx1; xm<=mx2; ++xm)
34049                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
34050                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
34051                         if (cval<min_val) min_val = cval;
34052                       }
34053                 res(x,y,z,c) = min_val;
34054               } _cimg_abort_catch_omp2
34055           if (boundary_conditions)
34056             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34057             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34058               cimg_abort_test2;
34059               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34060                 Tt min_val = cimg::type<Tt>::max();
34061                 for (int zm = -mz1; zm<=mz2; ++zm)
34062                   for (int ym = -my1; ym<=my2; ++ym)
34063                     for (int xm = -mx1; xm<=mx2; ++xm)
34064                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
34065                         const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
34066                         if (cval<min_val) min_val = cval;
34067                       }
34068                 res(x,y,z,c) = min_val;
34069               }
34070             } _cimg_abort_catch_omp2
34071           else
34072             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34073             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34074               cimg_abort_test2;
34075               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34076                 Tt min_val = cimg::type<Tt>::max();
34077                 for (int zm = -mz1; zm<=mz2; ++zm)
34078                   for (int ym = -my1; ym<=my2; ++ym)
34079                     for (int xm = -mx1; xm<=mx2; ++xm)
34080                       if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
34081                         const T cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
34082                         if (cval<min_val) min_val = cval;
34083                       }
34084                 res(x,y,z,c) = min_val;
34085               }
34086             } _cimg_abort_catch_omp2
34087         }
34088       } _cimg_abort_catch_omp
34089       cimg_abort_test;
34090       return res;
34091     }
34092 
34093     //! Erode image by a rectangular structuring element of specified size.
34094     /**
34095        \param sx Width of the structuring element.
34096        \param sy Height of the structuring element.
34097        \param sz Depth of the structuring element.
34098     **/
34099     CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
34100       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
34101       if (sx>1 && _width>1) { // Along X-axis.
34102         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;
34103         CImg<T> buf(L);
34104         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34105         cimg_forYZC(*this,y,z,c) {
34106           T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1;
34107           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34108           T cur = *ptrs; ptrs+=off; bool is_first = true;
34109           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34110             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }}
34111           *(ptrd++) = cur;
34112           if (ptrs>=ptrse) {
34113             T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34114           } else {
34115             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34116               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34117               *(ptrd++) = cur;
34118             }
34119             for (int p = L - s - 1; p>0; --p) {
34120               const T val = *ptrs; ptrs+=off;
34121               if (is_first) {
34122                 const T *nptrs = ptrs - off; cur = val;
34123                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
34124                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
34125               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34126               *(ptrd++) = cur;
34127             }
34128             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34129             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34130               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
34131             }
34132             *(ptrd--) = cur;
34133             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34134               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
34135             }
34136             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34137           }
34138         }
34139       }
34140 
34141       if (sy>1 && _height>1) { // Along Y-axis.
34142         const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
34143           s2 = _s2>L?L:_s2;
34144         CImg<T> buf(L);
34145         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34146         cimg_forXZC(*this,x,z,c) {
34147           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34148           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34149           T cur = *ptrs; ptrs+=off; bool is_first = true;
34150           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34151             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34152           }
34153           *(ptrd++) = cur;
34154           if (ptrs>=ptrse) {
34155             T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34156           } else {
34157             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34158               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34159               *(ptrd++) = cur;
34160             }
34161             for (int p = L - s - 1; p>0; --p) {
34162               const T val = *ptrs; ptrs+=off;
34163               if (is_first) {
34164                 const T *nptrs = ptrs - off; cur = val;
34165                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
34166                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
34167               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34168               *(ptrd++) = cur;
34169             }
34170             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34171             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34172               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
34173             }
34174             *(ptrd--) = cur;
34175             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34176               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
34177             }
34178             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34179           }
34180         }
34181       }
34182 
34183       if (sz>1 && _depth>1) { // Along Z-axis.
34184         const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
34185           s2 = _s2>L?L:_s2;
34186         CImg<T> buf(L);
34187         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34188         cimg_forXYC(*this,x,y,c) {
34189           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34190           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34191           T cur = *ptrs; ptrs+=off; bool is_first = true;
34192           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34193             const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34194           }
34195           *(ptrd++) = cur;
34196           if (ptrs>=ptrse) {
34197             T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34198           } else {
34199             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34200               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
34201               *(ptrd++) = cur;
34202             }
34203             for (int p = L - s - 1; p>0; --p) {
34204               const T val = *ptrs; ptrs+=off;
34205               if (is_first) {
34206                 const T *nptrs = ptrs - off; cur = val;
34207                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
34208                 nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
34209               } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34210               *(ptrd++) = cur;
34211             }
34212             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34213             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34214               const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
34215             }
34216             *(ptrd--) = cur;
34217             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34218               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
34219             }
34220             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34221           }
34222         }
34223       }
34224       return *this;
34225     }
34226 
34227     //! Erode image by a rectangular structuring element of specified size \newinstance.
34228     CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
34229       return (+*this).erode(sx,sy,sz);
34230     }
34231 
34232     //! Erode the image by a square structuring element of specified size.
34233     /**
34234        \param s Size of the structuring element.
34235     **/
34236     CImg<T>& erode(const unsigned int s) {
34237       return erode(s,s,s);
34238     }
34239 
34240     //! Erode the image by a square structuring element of specified size \newinstance.
34241     CImg<T> get_erode(const unsigned int s) const {
34242       return (+*this).erode(s);
34243     }
34244 
34245     //! Dilate image by a structuring element.
34246     /**
34247        \param kernel Structuring element.
34248        \param boundary_conditions Boundary conditions.
34249        \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
34250     **/
34251     template<typename t>
34252     CImg<T>& dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
34253                     const bool is_real=false) {
34254       if (is_empty() || !kernel) return *this;
34255       return get_dilate(kernel,boundary_conditions,is_real).move_to(*this);
34256     }
34257 
34258     //! Dilate image by a structuring element \newinstance.
34259     template<typename t>
34260     CImg<_cimg_Tt> get_dilate(const CImg<t>& kernel, const bool boundary_conditions=true,
34261                               const bool is_real=false) const {
34262       if (is_empty() || !kernel || (!is_real && kernel==0)) return *this;
34263       typedef _cimg_Tt Tt;
34264       CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
34265       const int
34266         mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2,
34267         mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1,
34268         mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
34269       const bool
34270         is_inner_parallel = _width*_height*_depth>=32768,
34271         is_outer_parallel = res.size()>=32768;
34272       cimg::unused(is_inner_parallel,is_outer_parallel);
34273       _cimg_abort_init_omp;
34274       cimg_abort_init;
34275       cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
34276       cimg_forC(res,c) _cimg_abort_try_omp {
34277         cimg_abort_test;
34278         const CImg<T> img = get_shared_channel(c%_spectrum);
34279         const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
34280         if (is_real) { // Real dilation
34281           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
34282           for (int z = mz1; z<mze; ++z)
34283             for (int y = my1; y<mye; ++y)
34284               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
34285                 cimg_abort_test2;
34286                 Tt max_val = cimg::type<Tt>::min();
34287                 for (int zm = -mz1; zm<=mz2; ++zm)
34288                   for (int ym = -my1; ym<=my2; ++ym)
34289                     for (int xm = -mx1; xm<=mx2; ++xm) {
34290                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
34291                       const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval);
34292                       if (cval>max_val) max_val = cval;
34293                     }
34294                 res(x,y,z,c) = max_val;
34295               } _cimg_abort_catch_omp2
34296           if (boundary_conditions)
34297             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34298             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34299               cimg_abort_test2;
34300               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34301                 Tt max_val = cimg::type<Tt>::min();
34302                 for (int zm = -mz1; zm<=mz2; ++zm)
34303                   for (int ym = -my1; ym<=my2; ++ym)
34304                     for (int xm = -mx1; xm<=mx2; ++xm) {
34305                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
34306                       const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval);
34307                       if (cval>max_val) max_val = cval;
34308                     }
34309                 res(x,y,z,c) = max_val;
34310               }
34311             } _cimg_abort_catch_omp2
34312           else
34313             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34314             cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 {
34315               cimg_abort_test2;
34316               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34317                 Tt max_val = cimg::type<Tt>::min();
34318                 for (int zm = -mz1; zm<=mz2; ++zm)
34319                   for (int ym = -my1; ym<=my2; ++ym)
34320                     for (int xm = -mx1; xm<=mx2; ++xm) {
34321                       const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
34322                       const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval);
34323                       if (cval>max_val) max_val = cval;
34324                     }
34325                 res(x,y,z,c) = max_val;
34326               }
34327             } _cimg_abort_catch_omp2
34328         } else { // Binary dilation
34329           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(is_inner_parallel))
34330           for (int z = mz1; z<mze; ++z)
34331             for (int y = my1; y<mye; ++y)
34332               for (int x = mx1; x<mxe; ++x) _cimg_abort_try_omp2 {
34333                 cimg_abort_test2;
34334                 Tt max_val = cimg::type<Tt>::min();
34335                 for (int zm = -mz1; zm<=mz2; ++zm)
34336                   for (int ym = -my1; ym<=my2; ++ym)
34337                     for (int xm = -mx1; xm<=mx2; ++xm)
34338                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
34339                         const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
34340                         if (cval>max_val) max_val = cval;
34341                       }
34342                 res(x,y,z,c) = max_val;
34343               } _cimg_abort_catch_omp2
34344           if (boundary_conditions)
34345             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34346             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34347               cimg_abort_test2;
34348               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34349                 Tt max_val = cimg::type<Tt>::min();
34350                 for (int zm = -mz1; zm<=mz2; ++zm)
34351                   for (int ym = -my1; ym<=my2; ++ym)
34352                     for (int xm = -mx1; xm<=mx2; ++xm)
34353                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
34354                         const T cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm);
34355                         if (cval>max_val) max_val = cval;
34356                       }
34357                 res(x,y,z,c) = max_val;
34358               }
34359             } _cimg_abort_catch_omp2
34360           else
34361             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(is_inner_parallel))
34362             cimg_forYZ(res,y,z) _cimg_abort_try_omp2 {
34363               cimg_abort_test2;
34364               for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
34365                 Tt max_val = cimg::type<Tt>::min();
34366                 for (int zm = -mz1; zm<=mz2; ++zm)
34367                   for (int ym = -my1; ym<=my2; ++ym)
34368                     for (int xm = -mx1; xm<=mx2; ++xm)
34369                       if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
34370                         const T cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0);
34371                         if (cval>max_val) max_val = cval;
34372                       }
34373                 res(x,y,z,c) = max_val;
34374               }
34375             } _cimg_abort_catch_omp2
34376         }
34377       } _cimg_abort_catch_omp
34378       cimg_abort_test;
34379       return res;
34380     }
34381 
34382     //! Dilate image by a rectangular structuring element of specified size.
34383     /**
34384        \param sx Width of the structuring element.
34385        \param sy Height of the structuring element.
34386        \param sz Depth of the structuring element.
34387     **/
34388     CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
34389       if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
34390       if (sx>1 && _width>1) { // Along X-axis.
34391         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;
34392         CImg<T> buf(L);
34393         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34394         cimg_forYZC(*this,y,z,c) {
34395           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34396           const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34397           T cur = *ptrs; ptrs+=off; bool is_first = true;
34398           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34399             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34400           }
34401           *(ptrd++) = cur;
34402           if (ptrs>=ptrse) {
34403             T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34404           } else {
34405             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34406               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34407               *(ptrd++) = cur;
34408             }
34409             for (int p = L - s - 1; p>0; --p) {
34410               const T val = *ptrs; ptrs+=off;
34411               if (is_first) {
34412                 const T *nptrs = ptrs - off; cur = val;
34413                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
34414                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
34415               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34416               *(ptrd++) = cur;
34417             }
34418             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34419             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34420               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
34421             }
34422             *(ptrd--) = cur;
34423             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34424               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
34425             }
34426             T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34427           }
34428         }
34429       }
34430 
34431       if (sy>1 && _height>1) { // Along Y-axis.
34432         const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
34433           s2 = _s2>L?L:_s2;
34434         CImg<T> buf(L);
34435         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34436         cimg_forXZC(*this,x,z,c) {
34437           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34438           const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34439           T cur = *ptrs; ptrs+=off; bool is_first = true;
34440           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34441             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34442           }
34443           *(ptrd++) = cur;
34444           if (ptrs>=ptrse) {
34445             T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34446           } else {
34447             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34448               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34449               *(ptrd++) = cur;
34450             }
34451             for (int p = L - s - 1; p>0; --p) {
34452               const T val = *ptrs; ptrs+=off;
34453               if (is_first) {
34454                 const T *nptrs = ptrs - off; cur = val;
34455                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
34456                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
34457               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34458               *(ptrd++) = cur;
34459             }
34460             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34461             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34462               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
34463             }
34464             *(ptrd--) = cur;
34465             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34466               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
34467             }
34468             T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34469           }
34470         }
34471       }
34472 
34473       if (sz>1 && _depth>1) { // Along Z-axis.
34474         const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
34475           s2 = _s2>L?L:_s2;
34476         CImg<T> buf(L);
34477         cimg_pragma_openmp(parallel for collapse(3) firstprivate(buf) if (size()>524288))
34478         cimg_forXYC(*this,x,y,c) {
34479           T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
34480           const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
34481           T cur = *ptrs; ptrs+=off; bool is_first = true;
34482           for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
34483             const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34484           }
34485           *(ptrd++) = cur;
34486           if (ptrs>=ptrse) {
34487             T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,x) { *pd = cur; pd+=off; }
34488           } else {
34489             for (int p = s1; p>0 && ptrd<=ptrde; --p) {
34490               const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
34491               *(ptrd++) = cur;
34492             }
34493             for (int p = L - s - 1; p>0; --p) {
34494               const T val = *ptrs; ptrs+=off;
34495               if (is_first) {
34496                 const T *nptrs = ptrs - off; cur = val;
34497                 for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
34498                 nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
34499               } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
34500               *(ptrd++) = cur;
34501             }
34502             ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
34503             for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
34504               const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
34505             }
34506             *(ptrd--) = cur;
34507             for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
34508               const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
34509             }
34510             T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
34511           }
34512         }
34513       }
34514       return *this;
34515     }
34516 
34517     //! Dilate image by a rectangular structuring element of specified size \newinstance.
34518     CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
34519       return (+*this).dilate(sx,sy,sz);
34520     }
34521 
34522     //! Dilate image by a square structuring element of specified size.
34523     /**
34524        \param s Size of the structuring element.
34525     **/
34526     CImg<T>& dilate(const unsigned int s) {
34527       return dilate(s,s,s);
34528     }
34529 
34530     //! Dilate image by a square structuring element of specified size \newinstance.
34531     CImg<T> get_dilate(const unsigned int s) const {
34532       return (+*this).dilate(s);
34533     }
34534 
34535     //! Compute watershed transform.
34536     /**
34537        \param priority Priority map.
34538        \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
34539        in 2d case, and between 6(false)- or 26(true)-connectivity in 3d case.
34540        \note Non-zero values of the instance instance are propagated to zero-valued ones according to
34541        specified the priority map.
34542     **/
34543     template<typename t>
34544     CImg<T>& watershed(const CImg<t>& priority, const bool is_high_connectivity=false) {
34545 #define _cimg_watershed_init(cond,X,Y,Z) \
34546       if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds)
34547 
34548 #define _cimg_watershed_propagate(cond,X,Y,Z) \
34549       if (cond) { \
34550         if ((*this)(X,Y,Z)) { \
34551           ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \
34552           d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \
34553           if (d<dmin) { dmin = d; nmin = ns; label = (*this)(xs,ys,zs); } \
34554         } else Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,n); \
34555       }
34556 
34557       if (is_empty()) return *this;
34558       if (!is_sameXYZ(priority))
34559         throw CImgArgumentException(_cimg_instance
34560                                     "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) "
34561                                     "have different dimensions.",
34562                                     cimg_instance,
34563                                     priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
34564       if (_spectrum!=1) {
34565         cimg_forC(*this,c)
34566           get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum));
34567         return *this;
34568       }
34569 
34570       CImg<uintT> labels(_width,_height,_depth,1,0), seeds(64,3);
34571       CImg<typename cimg::superset2<T,t,int>::type> Q;
34572       unsigned int sizeQ = 0;
34573       int px, nx, py, ny, pz, nz;
34574       bool is_px, is_nx, is_py, is_ny, is_pz, is_nz;
34575       const bool is_3d = _depth>1;
34576 
34577       // Find seed points and insert them in priority queue.
34578       unsigned int nb_seeds = 0;
34579       const T *ptrs = _data;
34580       cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3d version
34581         if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0);
34582         seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z;
34583         px = x - 1; nx = x + 1;
34584         py = y - 1; ny = y + 1;
34585         pz = z - 1; nz = z + 1;
34586         is_px = px>=0; is_nx = nx<width();
34587         is_py = py>=0; is_ny = ny<height();
34588         is_pz = pz>=0; is_nz = nz<depth();
34589         _cimg_watershed_init(is_px,px,y,z);
34590         _cimg_watershed_init(is_nx,nx,y,z);
34591         _cimg_watershed_init(is_py,x,py,z);
34592         _cimg_watershed_init(is_ny,x,ny,z);
34593         if (is_3d) {
34594           _cimg_watershed_init(is_pz,x,y,pz);
34595           _cimg_watershed_init(is_nz,x,y,nz);
34596         }
34597         if (is_high_connectivity) {
34598           _cimg_watershed_init(is_px && is_py,px,py,z);
34599           _cimg_watershed_init(is_nx && is_py,nx,py,z);
34600           _cimg_watershed_init(is_px && is_ny,px,ny,z);
34601           _cimg_watershed_init(is_nx && is_ny,nx,ny,z);
34602           if (is_3d) {
34603             _cimg_watershed_init(is_px && is_pz,px,y,pz);
34604             _cimg_watershed_init(is_nx && is_pz,nx,y,pz);
34605             _cimg_watershed_init(is_px && is_nz,px,y,nz);
34606             _cimg_watershed_init(is_nx && is_nz,nx,y,nz);
34607             _cimg_watershed_init(is_py && is_pz,x,py,pz);
34608             _cimg_watershed_init(is_ny && is_pz,x,ny,pz);
34609             _cimg_watershed_init(is_py && is_nz,x,py,nz);
34610             _cimg_watershed_init(is_ny && is_nz,x,ny,nz);
34611             _cimg_watershed_init(is_px && is_py && is_pz,px,py,pz);
34612             _cimg_watershed_init(is_nx && is_py && is_pz,nx,py,pz);
34613             _cimg_watershed_init(is_px && is_ny && is_pz,px,ny,pz);
34614             _cimg_watershed_init(is_nx && is_ny && is_pz,nx,ny,pz);
34615             _cimg_watershed_init(is_px && is_py && is_nz,px,py,nz);
34616             _cimg_watershed_init(is_nx && is_py && is_nz,nx,py,nz);
34617             _cimg_watershed_init(is_px && is_ny && is_nz,px,ny,nz);
34618             _cimg_watershed_init(is_nx && is_ny && is_nz,nx,ny,nz);
34619           }
34620         }
34621         labels(x,y,z) = nb_seeds;
34622       }
34623 
34624       // Start watershed computation.
34625       while (sizeQ) {
34626 
34627         // Get and remove point with maximal priority from the queue.
34628         const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
34629         const unsigned int n = labels(x,y,z);
34630         px = x - 1; nx = x + 1;
34631         py = y - 1; ny = y + 1;
34632         pz = z - 1; nz = z + 1;
34633         is_px = px>=0; is_nx = nx<width();
34634         is_py = py>=0; is_ny = ny<height();
34635         is_pz = pz>=0; is_nz = nz<depth();
34636 
34637         // Check labels of the neighbors.
34638         Q._priority_queue_remove(sizeQ);
34639 
34640         unsigned int xs, ys, zs, ns, nmin = 0;
34641         float d, dmin = cimg::type<float>::inf();
34642         T label = (T)0;
34643         _cimg_watershed_propagate(is_px,px,y,z);
34644         _cimg_watershed_propagate(is_nx,nx,y,z);
34645         _cimg_watershed_propagate(is_py,x,py,z);
34646         _cimg_watershed_propagate(is_ny,x,ny,z);
34647         if (is_3d) {
34648           _cimg_watershed_propagate(is_pz,x,y,pz);
34649           _cimg_watershed_propagate(is_nz,x,y,nz);
34650         }
34651         if (is_high_connectivity) {
34652           _cimg_watershed_propagate(is_px && is_py,px,py,z);
34653           _cimg_watershed_propagate(is_nx && is_py,nx,py,z);
34654           _cimg_watershed_propagate(is_px && is_ny,px,ny,z);
34655           _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z);
34656           if (is_3d) {
34657             _cimg_watershed_propagate(is_px && is_pz,px,y,pz);
34658             _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz);
34659             _cimg_watershed_propagate(is_px && is_nz,px,y,nz);
34660             _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz);
34661             _cimg_watershed_propagate(is_py && is_pz,x,py,pz);
34662             _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz);
34663             _cimg_watershed_propagate(is_py && is_nz,x,py,nz);
34664             _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz);
34665             _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz);
34666             _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz);
34667             _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz);
34668             _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz);
34669             _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz);
34670             _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz);
34671             _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz);
34672             _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz);
34673           }
34674         }
34675         (*this)(x,y,z) = label;
34676         labels(x,y,z) = ++nmin;
34677       }
34678       return *this;
34679     }
34680 
34681     //! Compute watershed transform \newinstance.
34682     template<typename t>
34683     CImg<T> get_watershed(const CImg<t>& priority, const bool is_high_connectivity=false) const {
34684       return (+*this).watershed(priority,is_high_connectivity);
34685     }
34686 
34687     // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
34688     template<typename tq, typename tv>
34689     bool _priority_queue_insert(CImg<tq>& is_queued, unsigned int& siz, const tv value,
34690                                 const unsigned int x, const unsigned int y, const unsigned int z,
34691                                 const unsigned int n=1) {
34692       if (is_queued(x,y,z)) return false;
34693       is_queued(x,y,z) = (tq)n;
34694       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
34695       (*this)(siz - 1,0) = (T)value;
34696       (*this)(siz - 1,1) = (T)x;
34697       (*this)(siz - 1,2) = (T)y;
34698       (*this)(siz - 1,3) = (T)z;
34699       for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
34700         cimg::swap((*this)(pos,0),(*this)(par,0));
34701         cimg::swap((*this)(pos,1),(*this)(par,1));
34702         cimg::swap((*this)(pos,2),(*this)(par,2));
34703         cimg::swap((*this)(pos,3),(*this)(par,3));
34704       }
34705       return true;
34706     }
34707 
34708     CImg<T>& _priority_queue_remove(unsigned int& siz) {
34709       (*this)(0,0) = (*this)(--siz,0);
34710       (*this)(0,1) = (*this)(siz,1);
34711       (*this)(0,2) = (*this)(siz,2);
34712       (*this)(0,3) = (*this)(siz,3);
34713       const float value = (*this)(0,0);
34714       for (unsigned int pos = 0, left = 0, right = 0;
34715            ((right=2*(pos + 1),(left=right - 1))<siz && value<(*this)(left,0)) ||
34716              (right<siz && value<(*this)(right,0));) {
34717         if (right<siz) {
34718           if ((*this)(left,0)>(*this)(right,0)) {
34719             cimg::swap((*this)(pos,0),(*this)(left,0));
34720             cimg::swap((*this)(pos,1),(*this)(left,1));
34721             cimg::swap((*this)(pos,2),(*this)(left,2));
34722             cimg::swap((*this)(pos,3),(*this)(left,3));
34723             pos = left;
34724           } else {
34725             cimg::swap((*this)(pos,0),(*this)(right,0));
34726             cimg::swap((*this)(pos,1),(*this)(right,1));
34727             cimg::swap((*this)(pos,2),(*this)(right,2));
34728             cimg::swap((*this)(pos,3),(*this)(right,3));
34729             pos = right;
34730           }
34731         } else {
34732           cimg::swap((*this)(pos,0),(*this)(left,0));
34733           cimg::swap((*this)(pos,1),(*this)(left,1));
34734           cimg::swap((*this)(pos,2),(*this)(left,2));
34735           cimg::swap((*this)(pos,3),(*this)(left,3));
34736           pos = left;
34737         }
34738       }
34739       return *this;
34740     }
34741 
34742     //! Apply recursive Deriche filter.
34743     /**
34744        \param sigma Standard deviation of the filter.
34745        \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>.
34746        \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
34747        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
34748     **/
34749     CImg<T>& deriche(const float sigma, const unsigned int order=0, const char axis='x',
34750                      const bool boundary_conditions=true) {
34751 #define _cimg_deriche_apply \
34752   CImg<Tfloat> Y(N); \
34753   Tfloat *ptrY = Y._data, yb = 0, yp = 0; \
34754   T xp = (T)0; \
34755   if (boundary_conditions) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \
34756   for (int m = 0; m<N; ++m) { \
34757     const T xc = *ptrX; ptrX+=off; \
34758     const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \
34759     xp = xc; yb = yp; yp = yc; \
34760   } \
34761   T xn = (T)0, xa = (T)0; \
34762   Tfloat yn = 0, ya = 0; \
34763   if (boundary_conditions) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \
34764   for (int n = N - 1; n>=0; --n) { \
34765     const T xc = *(ptrX-=off); \
34766     const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \
34767     xa = xn; xn = xc; ya = yn; yn = yc; \
34768     *ptrX = (T)(*(--ptrY)+yc); \
34769   }
34770       const char naxis = cimg::lowercase(axis);
34771       const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
34772       if (is_empty() || (nsigma<0.1f && !order)) return *this;
34773       const float
34774         nnsigma = nsigma<0.1f?0.1f:nsigma,
34775         alpha = 1.695f/nnsigma,
34776         ema = (float)std::exp(-alpha),
34777         ema2 = (float)std::exp(-2*alpha),
34778         b1 = -2*ema,
34779         b2 = ema2;
34780       float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
34781       switch (order) {
34782       case 0 : {
34783         const float k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2);
34784         a0 = k;
34785         a1 = k*(alpha - 1)*ema;
34786         a2 = k*(alpha + 1)*ema;
34787         a3 = -k*ema2;
34788       } break;
34789       case 1 : {
34790         const float k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema);
34791 	a0 = a3 = 0;
34792 	a1 = k*ema;
34793         a2 = -a1;
34794       } break;
34795       case 2 : {
34796         const float
34797           ea = (float)std::exp(-alpha),
34798           k = -(ema2 - 1)/(2*alpha*ema),
34799           kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea));
34800         a0 = kn;
34801         a1 = -kn*(1 + k*alpha)*ema;
34802         a2 = kn*(1 - k*alpha)*ema;
34803         a3 = -kn*ema2;
34804       } break;
34805       default :
34806         throw CImgArgumentException(_cimg_instance
34807                                     "deriche(): Invalid specified filter order %u "
34808                                     "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
34809                                     cimg_instance,
34810                                     order);
34811       }
34812       coefp = (a0 + a1)/(1 + b1 + b2);
34813       coefn = (a2 + a3)/(1 + b1 + b2);
34814       switch (naxis) {
34815       case 'x' : {
34816         const int N = width();
34817         const ulongT off = 1U;
34818         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
34819         cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
34820       } break;
34821       case 'y' : {
34822         const int N = height();
34823         const ulongT off = (ulongT)_width;
34824         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
34825         cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
34826       } break;
34827       case 'z' : {
34828         const int N = depth();
34829         const ulongT off = (ulongT)_width*_height;
34830         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
34831         cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
34832       } break;
34833       default : {
34834         const int N = spectrum();
34835         const ulongT off = (ulongT)_width*_height*_depth;
34836         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
34837         cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
34838       }
34839       }
34840       return *this;
34841     }
34842 
34843     //! Apply recursive Deriche filter \newinstance.
34844     CImg<Tfloat> get_deriche(const float sigma, const unsigned int order=0, const char axis='x',
34845                              const bool boundary_conditions=true) const {
34846       return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
34847     }
34848 
34849     // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()).
34850     /*
34851        \param ptr the pointer of the data
34852        \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3].
34853        \param N size of the data
34854        \param off the offset between two data point
34855        \param order the order of the filter 0 (smoothing), 1st derivtive, 2nd derivative, 3rd derivative
34856        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
34857        \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005).
34858     */
34859     static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off,
34860 				      const unsigned int order, const bool boundary_conditions) {
34861       double val[4] = { 0 };  // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..]
34862       const double
34863 	sumsq = filter[0], sum = sumsq * sumsq,
34864 	a1 = filter[1], a2 = filter[2], a3 = filter[3],
34865 	scaleM = 1.0 / ( (1.0 + a1 - a2 + a3) * (1.0 - a1 - a2 - a3) * (1.0 + a2 + (a1 - a3) * a3) );
34866       double M[9]; // Triggs matrix
34867       M[0] = scaleM * (-a3 * a1 + 1.0 - a3 * a3 - a2);
34868       M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1);
34869       M[2] = scaleM * a3 * (a1 + a3 * a2);
34870       M[3] = scaleM * (a1 + a3 * a2);
34871       M[4] = -scaleM * (a2 - 1.0) * (a2 + a3 * a1);
34872       M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.0);
34873       M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2);
34874       M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3);
34875       M[8] = scaleM * a3 * (a1 + a3 * a2);
34876       switch (order) {
34877       case 0 : {
34878 	const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0);
34879 	for (int pass = 0; pass<2; ++pass) {
34880 	  if (!pass) {
34881 	    for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0);
34882 	  } else {
34883 	    // apply Triggs boundary conditions
34884 	    const double
34885 	      uplus = iplus/(1.0 - a1 - a2 - a3), vplus = uplus/(1.0 - a1 - a2 - a3),
34886 	      unp  = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus;
34887 	    val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum;
34888 	    val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum;
34889 	    val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum;
34890 	    *data = (T)val[0];
34891 	    data -= off;
34892 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34893 	  }
34894 	  for (int n = pass; n<N; ++n) {
34895 	    val[0] = (*data);
34896 	    if (pass) val[0] *= sum;
34897 	    for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
34898 	    *data = (T)val[0];
34899 	    if (!pass) data += off; else data -= off;
34900 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34901 	  }
34902 	  if (!pass) data -= off;
34903 	}
34904       } break;
34905       case 1 : {
34906 	double x[3]; // [front,center,back]
34907 	for (int pass = 0; pass<2; ++pass) {
34908 	  if (!pass) {
34909 	    for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
34910 	    for (int k = 0; k<4; ++k) val[k] = 0;
34911 	  } else {
34912 	    // apply Triggs boundary conditions
34913 	    const double
34914 	      unp  = val[1], unp1 = val[2], unp2 = val[3];
34915 	    val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
34916 	    val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
34917 	    val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
34918 	    *data = (T)val[0];
34919 	    data -= off;
34920 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34921 	  }
34922 	  for (int n = pass; n<N - 1; ++n) {
34923 	    if (!pass) {
34924 	      x[0] = *(data + off);
34925 	      val[0] = 0.5f * (x[0] - x[2]);
34926 	    } else val[0] = (*data) * sum;
34927 	    for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
34928 	    *data = (T)val[0];
34929 	    if (!pass) {
34930 	      data += off;
34931 	      for (int k = 2; k>0; --k) x[k] = x[k - 1];
34932 	    } else { data-=off;}
34933 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34934 	  }
34935 	  *data = (T)0;
34936 	}
34937       } break;
34938       case 2: {
34939 	double x[3]; // [front,center,back]
34940 	for (int pass = 0; pass<2; ++pass) {
34941 	  if (!pass) {
34942 	    for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
34943 	    for (int k = 0; k<4; ++k) val[k] = 0;
34944 	  } else {
34945 	    // apply Triggs boundary conditions
34946 	    const double
34947 	      unp  = val[1], unp1 = val[2], unp2 = val[3];
34948 	    val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
34949 	    val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
34950 	    val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
34951 	    *data = (T)val[0];
34952 	    data -= off;
34953 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34954 	  }
34955 	  for (int n = pass; n<N - 1; ++n) {
34956 	    if (!pass) { x[0] = *(data + off); val[0] = (x[1] - x[2]); }
34957 	    else { x[0] = *(data - off); val[0] = (x[2] - x[1]) * sum; }
34958 	    for (int k = 1; k<4; ++k) val[0] += val[k]*filter[k];
34959 	    *data = (T)val[0];
34960 	    if (!pass) data += off; else data -= off;
34961 	    for (int k = 2; k>0; --k) x[k] = x[k - 1];
34962 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34963 	  }
34964 	  *data = (T)0;
34965 	}
34966       } break;
34967       case 3: {
34968 	double x[3]; // [front,center,back]
34969 	for (int pass = 0; pass<2; ++pass) {
34970 	  if (!pass) {
34971 	    for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
34972 	    for (int k = 0; k<4; ++k) val[k] = 0;
34973 	  } else {
34974 	    // apply Triggs boundary conditions
34975 	    const double
34976 	      unp = val[1], unp1 = val[2], unp2 = val[3];
34977 	    val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
34978 	    val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
34979 	    val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
34980 	    *data = (T)val[0];
34981 	    data -= off;
34982 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34983 	  }
34984 	  for (int n = pass; n<N - 1; ++n) {
34985 	    if (!pass) { x[0] = *(data + off); val[0] = (x[0] - 2*x[1] + x[2]); }
34986 	    else { x[0] = *(data - off); val[0] = 0.5f * (x[2] - x[0]) * sum; }
34987 	    for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
34988 	    *data = (T)val[0];
34989 	    if (!pass) data += off; else data -= off;
34990 	    for (int k = 2; k>0; --k) x[k] = x[k - 1];
34991 	    for (int k = 3; k>0; --k) val[k] = val[k - 1];
34992 	  }
34993 	  *data = (T)0;
34994 	}
34995       } break;
34996       }
34997     }
34998 
34999     //! Van Vliet recursive Gaussian filter.
35000     /**
35001        \param sigma standard deviation of the Gaussian filter
35002        \param order the order of the filter 0,1,2,3
35003        \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
35004        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
35005        \note dirichlet boundary condition has a strange behavior
35006 
35007        I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering.
35008        IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002.
35009 
35010        (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995)
35011 
35012        Boundary conditions (only for order 0) using Triggs matrix, from
35013        B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet
35014        recursive filtering. IEEE Trans. Signal Processing,
35015        vol. 54, pp. 2365-2367, 2006.
35016     **/
35017     CImg<T>& vanvliet(const float sigma, const unsigned int order, const char axis='x',
35018                       const bool boundary_conditions=true) {
35019       if (is_empty()) return *this;
35020       if (!cimg::type<T>::is_float())
35021         return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this);
35022       const char naxis = cimg::lowercase(axis);
35023       const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
35024       if (is_empty() || (nsigma<0.5f && !order)) return *this;
35025       const double
35026 	nnsigma = nsigma<0.5f?0.5f:nsigma,
35027 	m0 = 1.16680, m1 = 1.10783, m2 = 1.40586,
35028         m1sq = m1 * m1, m2sq = m2 * m2,
35029 	q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)),
35030 	qsq = q * q,
35031 	scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq),
35032 	b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale,
35033 	b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale,
35034 	b3 = -qsq * q / scale,
35035 	B = ( m0 * (m1sq + m2sq) ) / scale;
35036       double filter[4];
35037       filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3;
35038       switch (naxis) {
35039       case 'x' : {
35040         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35041 	cimg_forYZC(*this,y,z,c)
35042 	  _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions);
35043       } break;
35044       case 'y' : {
35045         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35046 	cimg_forXZC(*this,x,z,c)
35047 	  _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions);
35048       } break;
35049       case 'z' : {
35050         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35051 	cimg_forXYC(*this,x,y,c)
35052 	  _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height,
35053 				order,boundary_conditions);
35054       } break;
35055       default : {
35056         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35057 	cimg_forXYZ(*this,x,y,z)
35058 	  _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth,
35059 				order,boundary_conditions);
35060       }
35061       }
35062       return *this;
35063     }
35064 
35065     //! Blur image using Van Vliet recursive Gaussian filter. \newinstance.
35066     CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x',
35067                               const bool boundary_conditions=true) const {
35068       return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
35069     }
35070 
35071     //! Blur image.
35072     /**
35073        \param sigma_x Standard deviation of the blur, along the X-axis.
35074        \param sigma_y Standard deviation of the blur, along the Y-axis.
35075        \param sigma_z Standard deviation of the blur, along the Z-axis.
35076        \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
35077        \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel.
35078        \note
35079        - The blur is computed as a 0-order Deriche filter. This is not a gaussian blur.
35080        - This is a recursive algorithm, not depending on the values of the standard deviations.
35081        \see deriche(), vanvliet().
35082     **/
35083     CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z,
35084                   const bool boundary_conditions=true, const bool is_gaussian=false) {
35085       if (is_empty()) return *this;
35086       if (is_gaussian) {
35087         if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions);
35088         if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions);
35089         if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions);
35090       } else {
35091         if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
35092         if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
35093         if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
35094       }
35095       return *this;
35096     }
35097 
35098     //! Blur image \newinstance.
35099     CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z,
35100                           const bool boundary_conditions=true, const bool is_gaussian=false) const {
35101       return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian);
35102     }
35103 
35104     //! Blur image isotropically.
35105     /**
35106        \param sigma Standard deviation of the blur.
35107        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
35108        \param is_gaussian Use a gaussian kernel (VanVliet) is set, a pseudo-gaussian (Deriche) otherwise.
35109        \see deriche(), vanvliet().
35110     **/
35111     CImg<T>& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) {
35112       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
35113       return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);
35114     }
35115 
35116     //! Blur image isotropically \newinstance.
35117     CImg<Tfloat> get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=false) const {
35118       return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian);
35119     }
35120 
35121     //! Blur image anisotropically, directed by a field of diffusion tensors.
35122     /**
35123        \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing.
35124        \param amplitude Amplitude of the smoothing.
35125        \param dl Spatial discretization.
35126        \param da Angular discretization.
35127        \param gauss_prec Precision of the diffusion process.
35128        \param interpolation_type Interpolation scheme.
35129          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
35130        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
35131     **/
35132     template<typename t>
35133     CImg<T>& blur_anisotropic(const CImg<t>& G,
35134                               const float amplitude=60, const float dl=0.8f, const float da=30,
35135                               const float gauss_prec=2, const unsigned int interpolation_type=0,
35136                               const bool is_fast_approx=1) {
35137 
35138       // Check arguments and init variables
35139       if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
35140         throw CImgArgumentException(_cimg_instance
35141                                     "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
35142                                     cimg_instance,
35143                                     G._width,G._height,G._depth,G._spectrum,G._data);
35144       if (is_empty() || dl<0) return *this;
35145       const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100;
35146       unsigned int iamplitude = cimg::round(namplitude);
35147       const bool is_3d = (G._spectrum==6);
35148       T val_min, val_max = max_min(val_min);
35149       _cimg_abort_init_omp;
35150       cimg_abort_init;
35151 
35152       if (da<=0) {  // Iterated oriented Laplacians
35153         CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
35154         for (unsigned int iteration = 0; iteration<iamplitude; ++iteration) {
35155           Tfloat *ptrd = velocity._data, veloc_max = 0;
35156           if (is_3d) // 3d version
35157             cimg_forC(*this,c) {
35158               cimg_abort_test;
35159               CImg_3x3x3(I,Tfloat);
35160               cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
35161                 const Tfloat
35162                   ixx = Incc + Ipcc - 2*Iccc,
35163                   ixy = (Innc + Ippc - Inpc - Ipnc)/4,
35164                   ixz = (Incn + Ipcp - Incp - Ipcn)/4,
35165                   iyy = Icnc + Icpc - 2*Iccc,
35166                   iyz = (Icnn + Icpp - Icnp - Icpn)/4,
35167                   izz = Iccn + Iccp - 2*Iccc,
35168                   veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
35169                                    G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
35170                 *(ptrd++) = veloc;
35171                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
35172               }
35173             }
35174           else // 2d version
35175             cimg_forZC(*this,z,c) {
35176               cimg_abort_test;
35177               CImg_3x3(I,Tfloat);
35178               cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
35179                 const Tfloat
35180                   ixx = Inc + Ipc - 2*Icc,
35181                   ixy = (Inn + Ipp - Inp - Ipn)/4,
35182                   iyy = Icn + Icp - 2*Icc,
35183                   veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
35184                 *(ptrd++) = veloc;
35185                 if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
35186               }
35187             }
35188           if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
35189         }
35190       } else { // LIC-based smoothing.
35191         const ulongT whd = (ulongT)_width*_height*_depth;
35192         const float sqrt2amplitude = (float)std::sqrt(2*namplitude);
35193         const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
35194         CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
35195         int N = 0;
35196         if (is_3d) { // 3d version
35197           for (float phi = cimg::mod(180.0f,da)/2.0f; phi<=180; phi+=da) {
35198             const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
35199               da2 = datmp<1?360.0f:datmp;
35200             for (float theta = 0; theta<360; (theta+=da2),++N) {
35201               const float
35202                 thetar = (float)(theta*cimg::PI/180),
35203                 vx = (float)(std::cos(thetar)*std::cos(phir)),
35204                 vy = (float)(std::sin(thetar)*std::cos(phir)),
35205                 vz = (float)std::sin(phir);
35206               const t
35207                 *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
35208                 *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
35209               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);
35210               cimg_forXYZ(G,xg,yg,zg) {
35211                 const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
35212                 const float
35213                   u = (float)(a*vx + b*vy + c*vz),
35214                   v = (float)(b*vx + d*vy + e*vz),
35215                   w = (float)(c*vx + e*vy + f*vz),
35216                   n = 1e-5f + cimg::hypot(u,v,w),
35217                   dln = dl/n;
35218                 *(pd0++) = (Tfloat)(u*dln);
35219                 *(pd1++) = (Tfloat)(v*dln);
35220                 *(pd2++) = (Tfloat)(w*dln);
35221                 *(pd3++) = (Tfloat)n;
35222               }
35223 
35224               cimg_abort_test;
35225               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=2)
35226                                  firstprivate(val))
35227               cimg_forYZ(*this,y,z) _cimg_abort_try_omp2 {
35228                 cimg_abort_test2;
35229                 cimg_forX(*this,x) {
35230                   val.fill(0);
35231                   const float
35232                     n = (float)W(x,y,z,3),
35233                     fsigma = (float)(n*sqrt2amplitude),
35234                     fsigma2 = 2*fsigma*fsigma,
35235                     length = gauss_prec*fsigma;
35236                   float
35237                     S = 0,
35238                     X = (float)x,
35239                     Y = (float)y,
35240                     Z = (float)z;
35241                   switch (interpolation_type) {
35242                   case 0 : { // Nearest neighbor
35243                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
35244                       const int
35245                         cx = (int)(X + 0.5f),
35246                         cy = (int)(Y + 0.5f),
35247                         cz = (int)(Z + 0.5f);
35248                       const float
35249                         u = (float)W(cx,cy,cz,0),
35250                         v = (float)W(cx,cy,cz,1),
35251                         w = (float)W(cx,cy,cz,2);
35252                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
35253                       else {
35254                         const float coef = (float)std::exp(-l*l/fsigma2);
35255                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
35256                         S+=coef;
35257                       }
35258                       X+=u; Y+=v; Z+=w;
35259                     }
35260                   } break;
35261                   case 1 : { // Linear interpolation
35262                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
35263                       const float
35264                         u = (float)(W._linear_atXYZ(X,Y,Z,0)),
35265                         v = (float)(W._linear_atXYZ(X,Y,Z,1)),
35266                         w = (float)(W._linear_atXYZ(X,Y,Z,2));
35267                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
35268                       else {
35269                         const float coef = (float)std::exp(-l*l/fsigma2);
35270                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
35271                         S+=coef;
35272                       }
35273                       X+=u; Y+=v; Z+=w;
35274                     }
35275                   } break;
35276                   default : { // 2nd order Runge Kutta
35277                     for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
35278                       const float
35279                         u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
35280                         v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
35281                         w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
35282                         u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)),
35283                         v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)),
35284                         w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2));
35285                       if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
35286                       else {
35287                         const float coef = (float)std::exp(-l*l/fsigma2);
35288                         cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
35289                         S+=coef;
35290                       }
35291                       X+=u; Y+=v; Z+=w;
35292                     }
35293                   } break;
35294                   }
35295                   Tfloat *ptrd = res.data(x,y,z);
35296                   if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
35297                   else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
35298                 }
35299               } _cimg_abort_catch_omp2
35300             }
35301           }
35302         } else { // 2d LIC algorithm
35303           for (float theta = cimg::mod(360.0f,da)/2.0f; theta<360; (theta+=da),++N) {
35304             const float thetar = (float)(theta*cimg::PI/180),
35305               vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
35306             const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
35307             Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
35308             cimg_forXY(G,xg,yg) {
35309               const t a = *(pa++), b = *(pb++), c = *(pc++);
35310               const float
35311                 u = (float)(a*vx + b*vy),
35312                 v = (float)(b*vx + c*vy),
35313                 n = std::max(1e-5f,cimg::hypot(u,v)),
35314                 dln = dl/n;
35315               *(pd0++) = (Tfloat)(u*dln);
35316               *(pd1++) = (Tfloat)(v*dln);
35317               *(pd2++) = (Tfloat)n;
35318             }
35319 
35320             cimg_abort_test;
35321             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256 && _height>=2) firstprivate(val))
35322             cimg_forY(*this,y) _cimg_abort_try_omp2 {
35323               cimg_abort_test2;
35324               cimg_forX(*this,x) {
35325                 val.fill(0);
35326                 const float
35327                   n = (float)W(x,y,0,2),
35328                   fsigma = (float)(n*sqrt2amplitude),
35329                   fsigma2 = 2*fsigma*fsigma,
35330                   length = gauss_prec*fsigma;
35331                 float
35332                   S = 0,
35333                   X = (float)x,
35334                   Y = (float)y;
35335                 switch (interpolation_type) {
35336                 case 0 : { // Nearest-neighbor
35337                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
35338                     const int
35339                       cx = (int)(X + 0.5f),
35340                       cy = (int)(Y + 0.5f);
35341                     const float
35342                       u = (float)W(cx,cy,0,0),
35343                       v = (float)W(cx,cy,0,1);
35344                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
35345                     else {
35346                       const float coef = (float)std::exp(-l*l/fsigma2);
35347                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
35348                       S+=coef;
35349                     }
35350                     X+=u; Y+=v;
35351                   }
35352                 } break;
35353                 case 1 : { // Linear interpolation
35354                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
35355                     const float
35356                       u = (float)(W._linear_atXY(X,Y,0,0)),
35357                       v = (float)(W._linear_atXY(X,Y,0,1));
35358                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
35359                     else {
35360                       const float coef = (float)std::exp(-l*l/fsigma2);
35361                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
35362                       S+=coef;
35363                     }
35364                     X+=u; Y+=v;
35365                   }
35366                 } break;
35367                 default : { // 2nd-order Runge-kutta interpolation
35368                   for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
35369                     const float
35370                       u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
35371                       v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
35372                       u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)),
35373                       v = (float)(W._linear_atXY(X + u0,Y + v0,0,1));
35374                     if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
35375                     else {
35376                       const float coef = (float)std::exp(-l*l/fsigma2);
35377                       cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
35378                       S+=coef;
35379                     }
35380                     X+=u; Y+=v;
35381                   }
35382                 }
35383                 }
35384                 Tfloat *ptrd = res.data(x,y);
35385                 if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
35386                 else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
35387               }
35388             } _cimg_abort_catch_omp2
35389           }
35390         }
35391         const Tfloat *ptrs = res._data;
35392         cimg_for(*this,ptrd,T) {
35393           const Tfloat val = *(ptrs++)/N;
35394           *ptrd = val<val_min?val_min:(val>val_max?val_max:(T)val);
35395         }
35396       }
35397       cimg_abort_test;
35398       return *this;
35399     }
35400 
35401     //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance.
35402     template<typename t>
35403     CImg<Tfloat> get_blur_anisotropic(const CImg<t>& G,
35404                                       const float amplitude=60, const float dl=0.8f, const float da=30,
35405                                       const float gauss_prec=2, const unsigned int interpolation_type=0,
35406                                       const bool is_fast_approx=true) const {
35407       return CImg<Tfloat>(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
35408     }
35409 
35410     //! Blur image anisotropically, in an edge-preserving way.
35411     /**
35412        \param amplitude Amplitude of the smoothing.
35413        \param sharpness Sharpness.
35414        \param anisotropy Anisotropy.
35415        \param alpha Standard deviation of the gradient blur.
35416        \param sigma Standard deviation of the structure tensor blur.
35417        \param dl Spatial discretization.
35418        \param da Angular discretization.
35419        \param gauss_prec Precision of the diffusion process.
35420        \param interpolation_type Interpolation scheme.
35421          Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
35422        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
35423      **/
35424     CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
35425                               const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
35426                               const float gauss_prec=2, const unsigned int interpolation_type=0,
35427                               const bool is_fast_approx=true) {
35428       const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100;
35429       const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
35430       return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3),
35431                               amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
35432     }
35433 
35434     //! Blur image anisotropically, in an edge-preserving way \newinstance.
35435     CImg<Tfloat> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
35436                                       const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
35437                                       const float da=30, const float gauss_prec=2,
35438                                       const unsigned int interpolation_type=0,
35439                                       const bool is_fast_approx=true) const {
35440       return CImg<Tfloat>(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,
35441                                                         interpolation_type,is_fast_approx);
35442     }
35443 
35444     //! Blur image, with the joint bilateral filter.
35445     /**
35446        \param guide Image used to model the smoothing weights.
35447        \param sigma_x Amount of blur along the X-axis.
35448        \param sigma_y Amount of blur along the Y-axis.
35449        \param sigma_z Amount of blur along the Z-axis.
35450        \param sigma_r Amount of blur along the value axis.
35451        \param sampling_x Amount of downsampling along the X-axis used for the approximation.
35452          Defaults (0) to sigma_x.
35453        \param sampling_y Amount of downsampling along the Y-axis used for the approximation.
35454          Defaults (0) to sigma_y.
35455        \param sampling_z Amount of downsampling along the Z-axis used for the approximation.
35456          Defaults (0) to sigma_z.
35457        \param sampling_r Amount of downsampling along the value axis used for the approximation.
35458          Defaults (0) to sigma_r.
35459        \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
35460        (extended for 3d volumetric images).
35461        It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m
35462     **/
35463     template<typename t>
35464     CImg<T>& blur_bilateral(const CImg<t>& guide,
35465                             const float sigma_x, const float sigma_y,
35466                             const float sigma_z, const float sigma_r,
35467                             const float sampling_x, const float sampling_y,
35468                             const float sampling_z, const float sampling_r) {
35469       if (!is_sameXYZ(guide))
35470         throw CImgArgumentException(_cimg_instance
35471                                     "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
35472                                     cimg_instance,
35473                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
35474       if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this;
35475       T edge_min, edge_max = guide.max_min(edge_min);
35476       if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z);
35477       const float
35478         edge_delta = (float)(edge_max - edge_min),
35479         _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
35480         _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
35481         _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
35482         _sigma_r = sigma_r>=0?sigma_r:-sigma_r*(edge_max - edge_min)/100,
35483         _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.0f),
35484         _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.0f),
35485         _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.0f),
35486         _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256),
35487         derived_sigma_x = _sigma_x / _sampling_x,
35488         derived_sigma_y = _sigma_y / _sampling_y,
35489         derived_sigma_z = _sigma_z / _sampling_z,
35490         derived_sigma_r = _sigma_r / _sampling_r;
35491       const int
35492         padding_x = (int)(2*derived_sigma_x) + 1,
35493         padding_y = (int)(2*derived_sigma_y) + 1,
35494         padding_z = (int)(2*derived_sigma_z) + 1,
35495         padding_r = (int)(2*derived_sigma_r) + 1;
35496       const unsigned int
35497         bx = (unsigned int)((_width  - 1)/_sampling_x + 1 + 2*padding_x),
35498         by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y),
35499         bz = (unsigned int)((_depth  - 1)/_sampling_z + 1 + 2*padding_z),
35500         br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r);
35501       if (bx>0 || by>0 || bz>0 || br>0) {
35502         const bool is_3d = (_depth>1);
35503         if (is_3d) { // 3d version of the algorithm
35504           CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
35505           cimg_forC(*this,c) {
35506             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
35507             bgrid.fill(0); bgridw.fill(0);
35508             cimg_forXYZ(*this,x,y,z) {
35509               const T val = (*this)(x,y,z,c);
35510               const float edge = (float)_guide(x,y,z);
35511               const int
35512                 X = (int)cimg::round(x/_sampling_x) + padding_x,
35513                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
35514                 Z = (int)cimg::round(z/_sampling_z) + padding_z,
35515                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
35516               bgrid(X,Y,Z,R)+=(float)val;
35517               bgridw(X,Y,Z,R)+=1;
35518             }
35519             bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
35520             bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
35521 
35522             cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(size()>=4096))
35523             cimg_forXYZ(*this,x,y,z) {
35524               const float edge = (float)_guide(x,y,z);
35525               const float
35526                 X = x/_sampling_x + padding_x,
35527                 Y = y/_sampling_y + padding_y,
35528                 Z = z/_sampling_z + padding_z,
35529                 R = (edge - edge_min)/_sampling_r + padding_r;
35530               const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
35531               (*this)(x,y,z,c) = (T)(bval0/bval1);
35532             }
35533           }
35534         } else { // 2d version of the algorithm
35535           CImg<floatT> bgrid(bx,by,br,2);
35536           cimg_forC(*this,c) {
35537             const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
35538             bgrid.fill(0);
35539             cimg_forXY(*this,x,y) {
35540               const T val = (*this)(x,y,c);
35541               const float edge = (float)_guide(x,y);
35542               const int
35543                 X = (int)cimg::round(x/_sampling_x) + padding_x,
35544                 Y = (int)cimg::round(y/_sampling_y) + padding_y,
35545                 R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
35546               bgrid(X,Y,R,0)+=(float)val;
35547               bgrid(X,Y,R,1)+=1;
35548             }
35549             bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false);
35550 
35551             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(size()>=4096))
35552             cimg_forXY(*this,x,y) {
35553               const float edge = (float)_guide(x,y);
35554               const float
35555                 X = x/_sampling_x + padding_x,
35556                 Y = y/_sampling_y + padding_y,
35557                 R = (edge - edge_min)/_sampling_r + padding_r;
35558               const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
35559               (*this)(x,y,c) = (T)(bval0/bval1);
35560             }
35561           }
35562         }
35563       }
35564       return *this;
35565     }
35566 
35567     //! Blur image, with the joint bilateral filter \newinstance.
35568     template<typename t>
35569     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
35570                                     const float sigma_x, const float sigma_y,
35571                                     const float sigma_z, const float sigma_r,
35572                                     const float sampling_x, const float sampling_y,
35573                                     const float sampling_z, const float sampling_r) const {
35574       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,
35575                                                       sampling_x,sampling_y,sampling_z,sampling_r);
35576     }
35577 
35578     //! Blur image using the joint bilateral filter.
35579     /**
35580        \param guide Image used to model the smoothing weights.
35581        \param sigma_s Amount of blur along the XYZ-axes.
35582        \param sigma_r Amount of blur along the value axis.
35583        \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s.
35584        \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r.
35585     **/
35586     template<typename t>
35587     CImg<T>& blur_bilateral(const CImg<t>& guide,
35588                             const float sigma_s, const float sigma_r,
35589                             const float sampling_s=0, const float sampling_r=0) {
35590       const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
35591       return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r);
35592     }
35593 
35594     //! Blur image using the bilateral filter \newinstance.
35595     template<typename t>
35596     CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
35597                                     const float sigma_s, const float sigma_r,
35598                                     const float sampling_s=0, const float sampling_r=0) const {
35599       return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r);
35600     }
35601 
35602     // [internal] Apply a box filter (used by CImg<T>::boxfilter() and CImg<T>::blur_box()).
35603     /*
35604       \param ptr the pointer of the data
35605       \param N size of the data
35606       \param boxsize Size of the box filter (can be subpixel).
35607       \param off the offset between two data point
35608       \param order the order of the filter 0 (smoothing), 1st derivtive and 2nd derivative.
35609       \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
35610     */
35611     static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off,
35612                                      const int order, const bool boundary_conditions,
35613                                      const unsigned int nb_iter) {
35614       // Smooth.
35615       if (boxsize>1 && nb_iter) {
35616         const int w2 = (int)(boxsize - 1)/2;
35617         const unsigned int winsize = 2*w2 + 1U;
35618         const double frac = (boxsize - winsize)/2.;
35619         CImg<T> win(winsize);
35620         for (unsigned int iter = 0; iter<nb_iter; ++iter) {
35621           Tdouble sum = 0; // window sum
35622           for (int x = -w2; x<=w2; ++x) {
35623             win[x + w2] = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x);
35624             sum+=win[x + w2];
35625           }
35626           int ifirst = 0, ilast = 2*w2;
35627           T
35628             prev = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-w2 - 1),
35629             next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,w2 + 1);
35630           for (int x = 0; x < N - 1; ++x) {
35631             const double sum2 = sum + frac * (prev + next);
35632             ptr[x*off] = (T)(sum2/boxsize);
35633             prev = win[ifirst];
35634             sum-=prev;
35635             ifirst = (int)((ifirst + 1)%winsize);
35636             ilast = (int)((ilast + 1)%winsize);
35637             win[ilast] = next;
35638             sum+=next;
35639             next = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + w2 + 2);
35640           }
35641           const double sum2 = sum + frac * (prev + next);
35642           ptr[(N - 1)*off] = (T)(sum2/boxsize);
35643         }
35644       }
35645 
35646       // Derive.
35647       switch (order) {
35648       case 0 :
35649         break;
35650       case 1 : {
35651         Tfloat
35652           p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
35653           c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
35654           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
35655         for (int x = 0; x<N - 1; ++x) {
35656           ptr[x*off] = (T)((n-p)/2.0);
35657           p = c;
35658           c = n;
35659           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
35660         }
35661         ptr[(N - 1)*off] = (T)((n-p)/2.0);
35662       } break;
35663       case 2: {
35664         Tfloat
35665           p = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,-1),
35666           c = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,0),
35667           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,1);
35668         for (int x = 0; x<N - 1; ++x) {
35669           ptr[x*off] = (T)(n - 2*c + p);
35670           p = c;
35671           c = n;
35672           n = __cimg_blur_box_apply(ptr,N,off,boundary_conditions,x + 2);
35673         }
35674         ptr[(N - 1)*off] = (T)(n - 2*c + p);
35675       } break;
35676       }
35677     }
35678 
35679     static T __cimg_blur_box_apply(T *ptr, const int N, const ulongT off,
35680                                    const bool boundary_conditions, const int x) {
35681       if (x<0) return boundary_conditions?ptr[0]:T();
35682       if (x>=N) return boundary_conditions?ptr[(N - 1)*off]:T();
35683       return ptr[x*off];
35684     }
35685 
35686     // Apply box filter of order 0,1,2.
35687     /**
35688       \param boxsize Size of the box window (can be subpixel)
35689       \param order the order of the filter 0,1 or 2.
35690       \param axis  Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
35691       \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
35692       \param nb_iter Number of filter iterations.
35693     **/
35694     CImg<T>& boxfilter(const float boxsize, const int order, const char axis='x',
35695                        const bool boundary_conditions=true,
35696                        const unsigned int nb_iter=1) {
35697       if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this;
35698       const char naxis = cimg::lowercase(axis);
35699       const float nboxsize = boxsize>=0?boxsize:-boxsize*
35700         (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
35701       switch (naxis) {
35702       case 'x' : {
35703         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35704         cimg_forYZC(*this,y,z,c)
35705           _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter);
35706       } break;
35707       case 'y' : {
35708         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35709         cimg_forXZC(*this,x,z,c)
35710           _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter);
35711       } break;
35712       case 'z' : {
35713         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35714         cimg_forXYC(*this,x,y,c)
35715           _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter);
35716       } break;
35717       default : {
35718         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=256 && _height*_depth*_spectrum>=16))
35719         cimg_forXYZ(*this,x,y,z)
35720           _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth,
35721                                order,boundary_conditions,nb_iter);
35722       }
35723       }
35724       return *this;
35725     }
35726 
35727     // Apply box filter of order 0,1 or 2 \newinstance.
35728     CImg<Tfloat> get_boxfilter(const float boxsize, const int order, const char axis='x',
35729                                const bool boundary_conditions=true,
35730                                const unsigned int nb_iter=1) const {
35731       return CImg<Tfloat>(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter);
35732     }
35733 
35734     //! Blur image with a box filter.
35735     /**
35736        \param boxsize_x Size of the box window, along the X-axis (can be subpixel).
35737        \param boxsize_y Size of the box window, along the Y-axis (can be subpixel).
35738        \param boxsize_z Size of the box window, along the Z-axis (can be subpixel).
35739        \param boundary_conditions Boundary conditions. Can be <tt>{ false=dirichlet | true=neumann }</tt>.
35740        \param nb_iter Number of filter iterations.
35741        \note
35742        - This is a recursive algorithm, not depending on the values of the box kernel size.
35743        \see blur().
35744     **/
35745     CImg<T>& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
35746                       const bool boundary_conditions=true,
35747                       const unsigned int nb_iter=1) {
35748       if (is_empty()) return *this;
35749       if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter);
35750       if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter);
35751       if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter);
35752       return *this;
35753     }
35754 
35755     //! Blur image with a box filter \newinstance.
35756     CImg<Tfloat> get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
35757                               const bool boundary_conditions=true) const {
35758       return CImg<Tfloat>(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions);
35759     }
35760 
35761     //! Blur image with a box filter.
35762     /**
35763        \param boxsize Size of the box window (can be subpixel).
35764        \param boundary_conditions Boundary conditions. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.a
35765        \see deriche(), vanvliet().
35766     **/
35767     CImg<T>& blur_box(const float boxsize, const bool boundary_conditions=true) {
35768       const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100;
35769       return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions);
35770     }
35771 
35772     //! Blur image with a box filter \newinstance.
35773     CImg<Tfloat> get_blur_box(const float boxsize, const bool boundary_conditions=true) const {
35774       return CImg<Tfloat>(*this,false).blur_box(boxsize,boundary_conditions);
35775     }
35776 
35777     //! Blur image, with the image guided filter.
35778     /**
35779        \param guide Image used to guide the smoothing process.
35780        \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size.
35781        \param regularization Regularization parameter.
35782                              If negative, it is expressed as a percentage of the guide value range.
35783        \note This method implements the filtering algorithm described in:
35784        He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence,
35785        IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013
35786     **/
35787     template<typename t>
35788     CImg<T>& blur_guided(const CImg<t>& guide, const float radius, const float regularization) {
35789       return get_blur_guided(guide,radius,regularization).move_to(*this);
35790     }
35791 
35792     //! Blur image, with the image guided filter \newinstance.
35793     template<typename t>
35794     CImg<Tfloat> get_blur_guided(const CImg<t>& guide, const float radius, const float regularization) const {
35795       if (!is_sameXYZ(guide))
35796         throw CImgArgumentException(_cimg_instance
35797                                     "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
35798                                     cimg_instance,
35799                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
35800       if (is_empty() || !radius) return *this;
35801       const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100);
35802       float _regularization = regularization;
35803       if (regularization<0) {
35804         T edge_min, edge_max = guide.max_min(edge_min);
35805         if (edge_min==edge_max) return *this;
35806         _regularization = -regularization*(edge_max - edge_min)/100;
35807       }
35808       _regularization = std::max(_regularization,0.01f);
35809       const unsigned int psize = (unsigned int)(1 + 2*_radius);
35810       CImg<Tfloat>
35811         mean_p = get_blur_box(psize,true),
35812         mean_I = guide.get_blur_box(psize,true).resize(mean_p),
35813         cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I),
35814         var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(),
35815         &a = cov_Ip.div(var_I+=_regularization),
35816         &b = mean_p-=a.get_mul(mean_I);
35817       a.blur_box(psize,true);
35818       b.blur_box(psize,true);
35819       return a.mul(guide)+=b;
35820     }
35821 
35822     //! Blur image using patch-based space.
35823     /**
35824        \param sigma_s Amount of blur along the XYZ-axes.
35825        \param sigma_p Amount of blur along the value axis.
35826        \param patch_size Size of the patchs.
35827        \param lookup_size Size of the window to search similar patchs.
35828        \param smoothness Smoothness for the patch comparison.
35829        \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
35830     **/
35831     CImg<T>& blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
35832                         const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
35833       if (is_empty() || !patch_size || !lookup_size) return *this;
35834       return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
35835     }
35836 
35837     //! Blur image using patch-based space \newinstance.
35838     CImg<Tfloat> get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
35839                                 const unsigned int lookup_size=4, const float smoothness=0,
35840                                 const bool is_fast_approx=true) const {
35841 
35842 #define _cimg_blur_patch3d_fast(N) \
35843       cimg_for##N##XYZ(res,x,y,z) { \
35844         T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
35845         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
35846           x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
35847         float sum_weights = 0; \
35848         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \
35849           if (cimg::abs((Tfloat)img(x,y,z,0) - (Tfloat)img(p,q,r,0))<sigma_p3) { \
35850             T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
35851             float distance2 = 0; \
35852             pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
35853             distance2/=Pnorm; \
35854             const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
35855               alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
35856             sum_weights+=weight; \
35857             cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
35858           } \
35859         if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
35860         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
35861     }
35862 
35863 #define _cimg_blur_patch3d(N) \
35864       cimg_for##N##XYZ(res,x,y,z) { \
35865         T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
35866         const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
35867           x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
35868         float sum_weights = 0, weight_max = 0; \
35869         cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
35870           T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
35871           float distance2 = 0; \
35872           pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
35873           distance2/=Pnorm; \
35874           const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
35875             alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)std::exp(-alldist); \
35876           if (weight>weight_max) weight_max = weight; \
35877           sum_weights+=weight; \
35878           cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
35879         } \
35880         sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); \
35881         if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
35882         else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
35883       }
35884 
35885 #define _cimg_blur_patch2d_fast(N) \
35886         cimg_for##N##XY(res,x,y) { \
35887           T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
35888           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
35889           float sum_weights = 0; \
35890           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \
35891             if (cimg::abs((Tfloat)img(x,y,0,0) - (Tfloat)img(p,q,0,0))<sigma_p3) { \
35892               T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
35893               float distance2 = 0; \
35894               pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
35895               distance2/=Pnorm; \
35896               const float dx = (float)p - x, dy = (float)q - y, \
35897                 alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
35898               sum_weights+=weight; \
35899               cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
35900             } \
35901           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
35902           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
35903         }
35904 
35905 #define _cimg_blur_patch2d(N) \
35906         cimg_for##N##XY(res,x,y) { \
35907           T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
35908           const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
35909           float sum_weights = 0, weight_max = 0; \
35910           cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
35911             T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
35912             float distance2 = 0; \
35913             pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
35914             distance2/=Pnorm; \
35915             const float dx = (float)p - x, dy = (float)q - y, \
35916               alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)std::exp(-alldist); \
35917             if (weight>weight_max) weight_max = weight; \
35918             sum_weights+=weight; \
35919             cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
35920           } \
35921           sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); \
35922           if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
35923           else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
35924     }
35925 
35926       if (is_empty() || !patch_size || !lookup_size) return +*this;
35927       CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
35928       const CImg<T> _img = smoothness>0?get_blur(smoothness):CImg<Tfloat>(),&img = smoothness>0?_img:*this;
35929       CImg<T> P(patch_size*patch_size*_spectrum), Q(P);
35930       const float
35931         nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
35932         sigma_s2 = nsigma_s*nsigma_s, sigma_p2 = sigma_p*sigma_p, sigma_p3 = 3*sigma_p,
35933         Pnorm = P.size()*sigma_p2;
35934       const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
35935       const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
35936       cimg::unused(N2,N3);
35937       if (_depth>1) switch (patch_size) { // 3d
35938         case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
35939         case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
35940         default : {
35941           const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
35942           if (is_fast_approx)
35943             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(res._width>=32 && res._height*res._depth>=4)
35944                                private(P,Q))
35945             cimg_forXYZ(res,x,y,z) { // Fast
35946               P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
35947               const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
35948                 x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
35949               float sum_weights = 0;
35950               cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r)
35951                 if (cimg::abs((Tfloat)img(x,y,z,0) - (Tfloat)img(p,q,r,0))<sigma_p3) {
35952                   (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
35953                   const float
35954                     dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
35955                     distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
35956                     weight = distance2>3?0.0f:1.0f;
35957                   sum_weights+=weight;
35958                   cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
35959                 }
35960               if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
35961               else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
35962             } else
35963             cimg_pragma_openmp(parallel for collapse(2)
35964                                if (res._width>=32 && res._height*res._depth>=4) firstprivate(P,Q))
35965             cimg_forXYZ(res,x,y,z) { // Exact
35966               P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
35967               const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
35968                 x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
35969               float sum_weights = 0, weight_max = 0;
35970               cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
35971                 (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
35972                 const float
35973                   dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
35974                   distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
35975                   weight = (float)std::exp(-distance2);
35976                 if (weight>weight_max) weight_max = weight;
35977                 sum_weights+=weight;
35978                 cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
35979               }
35980               sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c);
35981               if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
35982               else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
35983             }
35984         }
35985         } else switch (patch_size) { // 2d
35986         case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
35987         case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
35988         case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
35989         case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
35990         case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
35991         case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
35992         case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
35993         case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
35994         default : { // Fast
35995           const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
35996           if (is_fast_approx)
35997             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=32 && res._height>=4) firstprivate(P,Q))
35998             cimg_forXY(res,x,y) { // 2d fast approximation.
35999               P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
36000               const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
36001               float sum_weights = 0;
36002               cimg_for_inXY(res,x0,y0,x1,y1,p,q)
36003                 if ((Tfloat)cimg::abs(img(x,y,0) - (Tfloat)img(p,q,0))<sigma_p3) {
36004                   (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
36005                   const float
36006                     dx = (float)x - p, dy = (float)y - q,
36007                     distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
36008                     weight = distance2>3?0.0f:1.0f;
36009                   sum_weights+=weight;
36010                   cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
36011                 }
36012               if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
36013               else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
36014             } else
36015             cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=32 && res._height>=4) firstprivate(P,Q))
36016             cimg_forXY(res,x,y) { // 2d exact algorithm.
36017               P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
36018               const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
36019               float sum_weights = 0, weight_max = 0;
36020               cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
36021                 (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
36022                 const float
36023                   dx = (float)x - p, dy = (float)y - q,
36024                   distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
36025                   weight = (float)std::exp(-distance2);
36026                 if (weight>weight_max) weight_max = weight;
36027                 sum_weights+=weight;
36028                 cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
36029               }
36030               sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c);
36031               if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
36032               else cimg_forC(res,c) res(x,y,0,c) = (Tfloat)((*this)(x,y,c));
36033             }
36034         }
36035         }
36036       return res;
36037     }
36038 
36039     //! Blur image with the median filter.
36040     /**
36041        \param n Size of the median filter.
36042        \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation.
36043     **/
36044     CImg<T>& blur_median(const unsigned int n, const float threshold=0) {
36045       if (!n) return *this;
36046       return get_blur_median(n,threshold).move_to(*this);
36047     }
36048 
36049     //! Blur image with the median filter \newinstance.
36050     CImg<T> get_blur_median(const unsigned int n, const float threshold=0) const {
36051       if (is_empty() || n<=1) return +*this;
36052       CImg<T> res(_width,_height,_depth,_spectrum);
36053       T *ptrd = res._data;
36054       cimg::unused(ptrd);
36055       const int hr = (int)n/2, hl = n - hr - 1;
36056       if (res._depth!=1) { // 3d
36057         if (threshold>0)
36058           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
36059           cimg_forXYZC(*this,x,y,z,c) { // With threshold.
36060             const int
36061               x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
36062               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
36063               nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
36064             const Tfloat val0 = (Tfloat)(*this)(x,y,z,c);
36065             CImg<T> values(n*n*n);
36066             unsigned int nb_values = 0;
36067             T *ptrd = values.data();
36068             cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r)
36069               if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(ptrd++) = (*this)(p,q,r,c); ++nb_values; }
36070             res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c);
36071           }
36072         else
36073           cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width>=16 && _height*_depth*_spectrum>=4))
36074           cimg_forXYZC(*this,x,y,z,c) { // Without threshold.
36075             const int
36076               x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
36077               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
36078               nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
36079             res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
36080           }
36081       } else {
36082         if (threshold>0)
36083           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=16 && _height*_spectrum>=4))
36084           cimg_forXYC(*this,x,y,c) { // With threshold.
36085             const int
36086               x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
36087               nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
36088                                         nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
36089             const Tfloat val0 = (Tfloat)(*this)(x,y,c);
36090             CImg<T> values(n*n);
36091             unsigned int nb_values = 0;
36092             T *ptrd = values.data();
36093             cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q)
36094               if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(ptrd++) = (*this)(p,q,c); ++nb_values; }
36095             res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c);
36096           }
36097         else {
36098           const int
36099             w1 = width() - 1, h1 = height() - 1,
36100             w2 = width() - 2, h2 = height() - 2,
36101             w3 = width() - 3, h3 = height() - 3,
36102             w4 = width() - 4, h4 = height() - 4;
36103           switch (n) { // Without threshold.
36104           case 3 : {
36105             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
36106             cimg_forC(*this,c) {
36107               CImg<T> I(9);
36108               cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T)
36109                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]);
36110               cimg_for_borderXY(*this,x,y,1)
36111                 res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c,
36112                                       std::min(w1,x + 1),std::min(h1,y + 1),0,c).median();
36113             }
36114           } break;
36115           case 5 : {
36116             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
36117             cimg_forC(*this,c) {
36118               CImg<T> I(25);
36119               cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T)
36120                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],
36121                                           I[5],I[6],I[7],I[8],I[9],
36122                                           I[10],I[11],I[12],I[13],I[14],
36123                                           I[15],I[16],I[17],I[18],I[19],
36124                                           I[20],I[21],I[22],I[23],I[24]);
36125               cimg_for_borderXY(*this,x,y,2)
36126                 res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c,
36127                                       std::min(w1,x + 2),std::min(h1,y + 2),0,c).median();
36128             }
36129           } break;
36130           case 7 : {
36131             cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
36132             cimg_forC(*this,c) {
36133               CImg<T> I(49);
36134               cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T)
36135                 res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],
36136                                           I[7],I[8],I[9],I[10],I[11],I[12],I[13],
36137                                           I[14],I[15],I[16],I[17],I[18],I[19],I[20],
36138                                           I[21],I[22],I[23],I[24],I[25],I[26],I[27],
36139                                           I[28],I[29],I[30],I[31],I[32],I[33],I[34],
36140                                           I[35],I[36],I[37],I[38],I[39],I[40],I[41],
36141                                           I[42],I[43],I[44],I[45],I[46],I[47],I[48]);
36142               cimg_for_borderXY(*this,x,y,3)
36143                 res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c,
36144                                       std::min(w1,x + 3),std::min(h1,y + 3),0,c).median();
36145             }
36146           } break;
36147           default : {
36148             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=16 && _height*_spectrum>=4))
36149             cimg_forXYC(*this,x,y,c) {
36150               const int
36151                 x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
36152                 nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
36153                                           nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
36154               res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
36155             }
36156           }
36157           }
36158         }
36159       }
36160       return res;
36161     }
36162 
36163     //! Sharpen image.
36164     /**
36165        \param amplitude Sharpening amplitude
36166        \param sharpen_type Select sharpening method. Can be <tt>{ false=inverse diffusion | true=shock filters }</tt>.
36167        \param edge Edge threshold (shock filters only).
36168        \param alpha Gradient smoothness (shock filters only).
36169        \param sigma Tensor smoothness (shock filters only).
36170     **/
36171     CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
36172                      const float alpha=0, const float sigma=0) {
36173       if (is_empty()) return *this;
36174       T val_min, val_max = max_min(val_min);
36175       const float nedge = edge/2;
36176       CImg<Tfloat> velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum);
36177 
36178       if (_depth>1) { // 3d
36179         if (sharpen_type) { // Shock filters.
36180           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
36181           if (sigma>0) G.blur(sigma);
36182           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=32 && _height*_depth>=16))
36183           cimg_forYZ(G,y,z) {
36184             Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1),
36185               *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3);
36186             CImg<Tfloat> val, vec;
36187             cimg_forX(G,x) {
36188               G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
36189               if (val[0]<0) val[0] = 0;
36190               if (val[1]<0) val[1] = 0;
36191               if (val[2]<0) val[2] = 0;
36192               *(ptrG0++) = vec(0,0);
36193               *(ptrG1++) = vec(0,1);
36194               *(ptrG2++) = vec(0,2);
36195               *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge);
36196             }
36197           }
36198           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=512 && _spectrum>=2))
36199           cimg_forC(*this,c) {
36200             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
36201             CImg_3x3x3(I,Tfloat);
36202             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
36203               const Tfloat
36204                 u = G(x,y,z,0),
36205                 v = G(x,y,z,1),
36206                 w = G(x,y,z,2),
36207                 amp = G(x,y,z,3),
36208                 ixx = Incc + Ipcc - 2*Iccc,
36209                 ixy = (Innc + Ippc - Inpc - Ipnc)/4,
36210                 ixz = (Incn + Ipcp - Incp - Ipcn)/4,
36211                 iyy = Icnc + Icpc - 2*Iccc,
36212                 iyz = (Icnn + Icpp - Icnp - Icpn)/4,
36213                 izz = Iccn + Iccp - 2*Iccc,
36214                 ixf = Incc - Iccc,
36215                 ixb = Iccc - Ipcc,
36216                 iyf = Icnc - Iccc,
36217                 iyb = Iccc - Icpc,
36218                 izf = Iccn - Iccc,
36219                 izb = Iccc - Iccp,
36220                 itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
36221                 it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
36222                 veloc = -amp*cimg::sign(itt)*cimg::abs(it);
36223               *(ptrd++) = veloc;
36224               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
36225             }
36226             _veloc_max[c] = veloc_max;
36227           }
36228         } else  // Inverse diffusion.
36229           cimg_forC(*this,c) {
36230             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
36231             CImg_3x3x3(I,Tfloat);
36232             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
36233               const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
36234               *(ptrd++) = veloc;
36235               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
36236             }
36237             _veloc_max[c] = veloc_max;
36238           }
36239       } else { // 2d.
36240         if (sharpen_type) { // Shock filters.
36241           CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
36242           if (sigma>0) G.blur(sigma);
36243           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=32 && _height>=16))
36244           cimg_forY(G,y) {
36245             CImg<Tfloat> val, vec;
36246             Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2);
36247             cimg_forX(G,x) {
36248               G.get_tensor_at(x,y).symmetric_eigen(val,vec);
36249               if (val[0]<0) val[0] = 0;
36250               if (val[1]<0) val[1] = 0;
36251               *(ptrG0++) = vec(0,0);
36252               *(ptrG1++) = vec(0,1);
36253               *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
36254             }
36255           }
36256           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512 && _spectrum>=2))
36257           cimg_forC(*this,c) {
36258             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
36259             CImg_3x3(I,Tfloat);
36260             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
36261               const Tfloat
36262                 u = G(x,y,0),
36263                 v = G(x,y,1),
36264                 amp = G(x,y,2),
36265                 ixx = Inc + Ipc - 2*Icc,
36266                 ixy = (Inn + Ipp - Inp - Ipn)/4,
36267                 iyy = Icn + Icp - 2*Icc,
36268                 ixf = Inc - Icc,
36269                 ixb = Icc - Ipc,
36270                 iyf = Icn - Icc,
36271                 iyb = Icc - Icp,
36272                 itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
36273                 it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
36274                 veloc = -amp*cimg::sign(itt)*cimg::abs(it);
36275               *(ptrd++) = veloc;
36276               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
36277             }
36278             _veloc_max[c] = veloc_max;
36279           }
36280         } else // Inverse diffusion.
36281           cimg_forC(*this,c) {
36282             Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
36283             CImg_3x3(I,Tfloat);
36284             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
36285               const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
36286               *(ptrd++) = veloc;
36287               if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
36288             }
36289             _veloc_max[c] = veloc_max;
36290           }
36291       }
36292       const Tfloat veloc_max = _veloc_max.max();
36293       if (veloc_max<=0) return *this;
36294       return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
36295     }
36296 
36297     //! Sharpen image \newinstance.
36298     CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
36299                         const float alpha=0, const float sigma=0) const {
36300       return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
36301     }
36302 
36303     //! Return image gradient.
36304     /**
36305        \param axes Axes considered for the gradient computation, as a C-string (e.g "xy").
36306        \param scheme = Numerical scheme used for the gradient computation:
36307        - -1 = Backward finite differences
36308        - 0 = Centered finite differences
36309        - 1 = Forward finite differences
36310        - 2 = Using Sobel kernels
36311        - 3 = Using rotation invariant kernels
36312        - 4 = Using Deriche recusrsive filter.
36313        - 5 = Using Van Vliet recusrsive filter.
36314     **/
36315     CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=3) const {
36316       CImgList<Tfloat> grad(2,_width,_height,_depth,_spectrum);
36317       bool is_3d = false;
36318       if (axes) {
36319         for (unsigned int a = 0; axes[a]; ++a) {
36320           const char axis = cimg::lowercase(axes[a]);
36321           switch (axis) {
36322           case 'x' : case 'y' : break;
36323           case 'z' : is_3d = true; break;
36324           default :
36325             throw CImgArgumentException(_cimg_instance
36326                                         "get_gradient(): Invalid specified axis '%c'.",
36327                                         cimg_instance,
36328                                         axis);
36329           }
36330         }
36331       } else is_3d = (_depth>1);
36332       if (is_3d) {
36333         CImg<Tfloat>(_width,_height,_depth,_spectrum).move_to(grad);
36334         switch (scheme) { // 3d.
36335         case -1 : { // Backward finite differences.
36336           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36337           cimg_forC(*this,c) {
36338             const ulongT off = (ulongT)c*_width*_height*_depth;
36339             Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off;
36340             CImg_3x3x3(I,Tfloat);
36341             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
36342               *(ptrd0++) = Iccc - Ipcc;
36343               *(ptrd1++) = Iccc - Icpc;
36344               *(ptrd2++) = Iccc - Iccp;
36345             }
36346           }
36347         } break;
36348         case 1 : { // Forward finite differences.
36349           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36350           cimg_forC(*this,c) {
36351             const ulongT off = (ulongT)c*_width*_height*_depth;
36352             Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off;
36353             CImg_2x2x2(I,Tfloat);
36354             cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) {
36355               *(ptrd0++) = Incc - Iccc;
36356               *(ptrd1++) = Icnc - Iccc;
36357               *(ptrd2++) = Iccn - Iccc;
36358             }
36359           }
36360         } break;
36361         case 4 : { // Deriche filter with low standard variation.
36362           grad[0] = get_deriche(0,1,'x');
36363           grad[1] = get_deriche(0,1,'y');
36364           grad[2] = get_deriche(0,1,'z');
36365         } break;
36366         case 5 : { // Van Vliet filter with low standard variation.
36367           grad[0] = get_vanvliet(0,1,'x');
36368           grad[1] = get_vanvliet(0,1,'y');
36369           grad[2] = get_vanvliet(0,1,'z');
36370         } break;
36371         default : { // Central finite differences.
36372           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36373           cimg_forC(*this,c) {
36374             const ulongT off = (ulongT)c*_width*_height*_depth;
36375             Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off, *ptrd2 = grad[2]._data + off;
36376             CImg_3x3x3(I,Tfloat);
36377             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
36378               *(ptrd0++) = (Incc - Ipcc)/2;
36379               *(ptrd1++) = (Icnc - Icpc)/2;
36380               *(ptrd2++) = (Iccn - Iccp)/2;
36381             }
36382           }
36383         }
36384         }
36385       } else switch (scheme) { // 2d.
36386       case -1 : { // Backward finite differences.
36387         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36388         cimg_forZC(*this,z,c) {
36389           const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
36390           Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
36391           CImg_3x3(I,Tfloat);
36392           cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
36393             *(ptrd0++) = Icc - Ipc;
36394             *(ptrd1++) = Icc - Icp;
36395           }
36396         }
36397       } break;
36398       case 1 : { // Forward finite differences.
36399         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36400         cimg_forZC(*this,z,c) {
36401           const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
36402           Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
36403           CImg_2x2(I,Tfloat);
36404           cimg_for2x2(*this,x,y,z,c,I,Tfloat) {
36405             *(ptrd0++) = Inc - Icc;
36406             *(ptrd1++) = Icn - Icc;
36407           }
36408         }
36409       } break;
36410       case 2 : { // Sobel scheme.
36411         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36412         cimg_forZC(*this,z,c) {
36413           const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
36414           Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
36415           CImg_3x3(I,Tfloat);
36416           cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
36417             *(ptrd0++) = -Ipp - 2*Ipc - Ipn + Inp + 2*Inc + Inn;
36418             *(ptrd1++) = -Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn;
36419           }
36420         }
36421       } break;
36422       case 3 : { // Rotation invariant kernel.
36423         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36424         cimg_forZC(*this,z,c) {
36425           const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
36426           Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
36427           CImg_3x3(I,Tfloat);
36428           const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f) - 1));
36429           cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
36430             *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
36431             *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
36432           }
36433         }
36434       } break;
36435       case 4 : { // Van Vliet filter with low standard variation
36436         grad[0] = get_deriche(0,1,'x');
36437         grad[1] = get_deriche(0,1,'y');
36438       } break;
36439       case 5 : { // Deriche filter with low standard variation
36440         grad[0] = get_vanvliet(0,1,'x');
36441         grad[1] = get_vanvliet(0,1,'y');
36442       } break;
36443       default : { // Central finite differences
36444         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36445         cimg_forZC(*this,z,c) {
36446           const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
36447           Tfloat *ptrd0 = grad[0]._data + off, *ptrd1 = grad[1]._data + off;
36448           CImg_3x3(I,Tfloat);
36449           cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
36450             *(ptrd0++) = (Inc - Ipc)/2;
36451             *(ptrd1++) = (Icn - Icp)/2;
36452           }
36453         }
36454       }
36455       }
36456       if (!axes) return grad;
36457       CImgList<Tfloat> res;
36458       for (unsigned int l = 0; axes[l]; ++l) {
36459         const char axis = cimg::lowercase(axes[l]);
36460         switch (axis) {
36461         case 'x' : res.insert(grad[0]); break;
36462         case 'y' : res.insert(grad[1]); break;
36463         case 'z' : res.insert(grad[2]); break;
36464         }
36465       }
36466       grad.assign();
36467       return res;
36468     }
36469 
36470     //! Return image hessian.
36471     /**
36472        \param axes Axes considered for the hessian computation, as a C-string (e.g "xy").
36473     **/
36474     CImgList<Tfloat> get_hessian(const char *const axes=0) const {
36475       CImgList<Tfloat> res;
36476       const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz";
36477       if (!axes) naxes = _depth>1?def_axes3d:def_axes2d;
36478       const unsigned int lmax = (unsigned int)std::strlen(naxes);
36479       if (lmax%2)
36480         throw CImgArgumentException(_cimg_instance
36481                                     "get_hessian(): Invalid specified axes '%s'.",
36482                                     cimg_instance,
36483                                     naxes);
36484 
36485       res.assign(lmax/2,_width,_height,_depth,_spectrum);
36486       if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d
36487 
36488         cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36489         cimg_forC(*this,c) {
36490           const ulongT off = (ulongT)c*_width*_height*_depth;
36491           Tfloat
36492             *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off,
36493             *ptrd3 = res[3]._data + off, *ptrd4 = res[4]._data + off, *ptrd5 = res[5]._data + off;
36494           CImg_3x3x3(I,Tfloat);
36495           cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
36496             *(ptrd0++) = Ipcc + Incc - 2*Iccc;          // Ixx
36497             *(ptrd1++) = (Ippc + Innc - Ipnc - Inpc)/4; // Ixy
36498             *(ptrd2++) = (Ipcp + Incn - Ipcn - Incp)/4; // Ixz
36499             *(ptrd3++) = Icpc + Icnc - 2*Iccc;          // Iyy
36500             *(ptrd4++) = (Icpp + Icnn - Icpn - Icnp)/4; // Iyz
36501             *(ptrd5++) = Iccn + Iccp - 2*Iccc;          // Izz
36502           }
36503         }
36504       } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // 2d
36505         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36506         cimg_forZC(*this,z,c) {
36507           const ulongT off = (ulongT)c*_width*_height*_depth + z*_width*_height;
36508           Tfloat *ptrd0 = res[0]._data + off, *ptrd1 = res[1]._data + off, *ptrd2 = res[2]._data + off;
36509           CImg_3x3(I,Tfloat);
36510           cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
36511             *(ptrd0++) = Ipc + Inc - 2*Icc;         // Ixx
36512             *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy
36513             *(ptrd2++) = Icp + Icn - 2*Icc;         // Iyy
36514           }
36515         }
36516       } else for (unsigned int l = 0; l<lmax; ) { // Version with custom axes.
36517           const unsigned int l2 = l/2;
36518           char axis1 = naxes[l++], axis2 = naxes[l++];
36519           if (axis1>axis2) cimg::swap(axis1,axis2);
36520           bool valid_axis = false;
36521           if (axis1=='x' && axis2=='x') { // Ixx
36522             valid_axis = true;
36523             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36524             cimg_forZC(*this,z,c) {
36525               Tfloat *ptrd = res[l2].data(0,0,z,c);
36526               CImg_3x3(I,Tfloat);
36527               cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc;
36528             }
36529           }
36530           else if (axis1=='x' && axis2=='y') { // Ixy
36531             valid_axis = true;
36532             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36533             cimg_forZC(*this,z,c) {
36534               Tfloat *ptrd = res[l2].data(0,0,z,c);
36535               CImg_3x3(I,Tfloat);
36536               cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4;
36537             }
36538           }
36539           else if (axis1=='x' && axis2=='z') { // Ixz
36540             valid_axis = true;
36541             cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36542             cimg_forC(*this,c) {
36543               Tfloat *ptrd = res[l2].data(0,0,0,c);
36544               CImg_3x3x3(I,Tfloat);
36545               cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4;
36546             }
36547           }
36548           else if (axis1=='y' && axis2=='y') { // Iyy
36549             valid_axis = true;
36550             cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36551             cimg_forZC(*this,z,c) {
36552               Tfloat *ptrd = res[l2].data(0,0,z,c);
36553               CImg_3x3(I,Tfloat);
36554               cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc;
36555             }
36556           }
36557           else if (axis1=='y' && axis2=='z') { // Iyz
36558             valid_axis = true;
36559             cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36560             cimg_forC(*this,c) {
36561               Tfloat *ptrd = res[l2].data(0,0,0,c);
36562               CImg_3x3x3(I,Tfloat);
36563               cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4;
36564             }
36565           }
36566           else if (axis1=='z' && axis2=='z') { // Izz
36567             valid_axis = true;
36568             cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36569             cimg_forC(*this,c) {
36570               Tfloat *ptrd = res[l2].data(0,0,0,c);
36571               CImg_3x3x3(I,Tfloat);
36572               cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc;
36573             }
36574           }
36575           else if (!valid_axis)
36576             throw CImgArgumentException(_cimg_instance
36577                                         "get_hessian(): Invalid specified axes '%s'.",
36578                                         cimg_instance,
36579                                         naxes);
36580         }
36581       return res;
36582     }
36583 
36584     //! Compute image laplacian.
36585     CImg<T>& laplacian() {
36586       return get_laplacian().move_to(*this);
36587     }
36588 
36589     //! Compute image laplacian \newinstance.
36590     CImg<Tfloat> get_laplacian() const {
36591       if (is_empty()) return CImg<Tfloat>();
36592       CImg<Tfloat> res(_width,_height,_depth,_spectrum);
36593       if (_depth>1) { // 3d
36594         cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36595         cimg_forC(*this,c) {
36596           Tfloat *ptrd = res.data(0,0,0,c);
36597           CImg_3x3x3(I,Tfloat);
36598           cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
36599         }
36600       } else if (_height>1) { // 2d
36601         cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36602         cimg_forC(*this,c) {
36603           Tfloat *ptrd = res.data(0,0,0,c);
36604           CImg_3x3(I,Tfloat);
36605           cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
36606         }
36607       } else { // 1d
36608         cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=1048576 && _height*_depth*_spectrum>=2))
36609         cimg_forC(*this,c) {
36610           Tfloat *ptrd = res.data(0,0,0,c);
36611           CImg_3x3(I,Tfloat);
36612           cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc;
36613         }
36614       }
36615       return res;
36616     }
36617 
36618     //! Compute the structure tensor field of an image.
36619     /**
36620        \param is_fwbw_scheme scheme. Can be <tt>{ false=centered | true=forward-backward }</tt>
36621     **/
36622     CImg<T>& structure_tensors(const bool is_fwbw_scheme=false) {
36623       return get_structure_tensors(is_fwbw_scheme).move_to(*this);
36624     }
36625 
36626     //! Compute the structure tensor field of an image \newinstance.
36627     CImg<Tfloat> get_structure_tensors(const bool is_fwbw_scheme=false) const {
36628       if (is_empty()) return *this;
36629       CImg<Tfloat> res;
36630       if (_depth>1) { // 3d
36631         res.assign(_width,_height,_depth,6,0);
36632         if (!is_fwbw_scheme) { // Classical central finite differences
36633           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36634           cimg_forC(*this,c) {
36635             Tfloat
36636               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
36637               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
36638             CImg_3x3x3(I,Tfloat);
36639             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
36640               const Tfloat
36641                 ix = (Incc - Ipcc)/2,
36642                 iy = (Icnc - Icpc)/2,
36643                 iz = (Iccn - Iccp)/2;
36644               *(ptrd0++)+=ix*ix;
36645               *(ptrd1++)+=ix*iy;
36646               *(ptrd2++)+=ix*iz;
36647               *(ptrd3++)+=iy*iy;
36648               *(ptrd4++)+=iy*iz;
36649               *(ptrd5++)+=iz*iz;
36650             }
36651           }
36652         } else { // Forward/backward finite differences.
36653           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=1048576 && _spectrum>=2))
36654           cimg_forC(*this,c) {
36655             Tfloat
36656               *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
36657               *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
36658             CImg_3x3x3(I,Tfloat);
36659             cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
36660               const Tfloat
36661                 ixf = Incc - Iccc, ixb = Iccc - Ipcc,
36662                 iyf = Icnc - Iccc, iyb = Iccc - Icpc,
36663                 izf = Iccn - Iccc, izb = Iccc - Iccp;
36664               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
36665               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
36666               *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
36667               *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
36668               *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
36669               *(ptrd5++)+=(izf*izf + izb*izb)/2;
36670             }
36671           }
36672         }
36673       } else { // 2d
36674         res.assign(_width,_height,_depth,3,0);
36675         if (!is_fwbw_scheme) { // Classical central finite differences
36676           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36677           cimg_forC(*this,c) {
36678             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
36679             CImg_3x3(I,Tfloat);
36680             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
36681               const Tfloat
36682                 ix = (Inc - Ipc)/2,
36683                 iy = (Icn - Icp)/2;
36684               *(ptrd0++)+=ix*ix;
36685               *(ptrd1++)+=ix*iy;
36686               *(ptrd2++)+=iy*iy;
36687             }
36688           }
36689         } else { // Forward/backward finite differences (version 2).
36690           cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=1048576 && _depth*_spectrum>=2))
36691           cimg_forC(*this,c) {
36692             Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
36693             CImg_3x3(I,Tfloat);
36694             cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
36695               const Tfloat
36696                 ixf = Inc - Icc, ixb = Icc - Ipc,
36697                 iyf = Icn - Icc, iyb = Icc - Icp;
36698               *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
36699               *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
36700               *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
36701             }
36702           }
36703         }
36704       }
36705       return res;
36706     }
36707 
36708     //! Compute field of diffusion tensors for edge-preserving smoothing.
36709     /**
36710        \param sharpness Sharpness
36711        \param anisotropy Anisotropy
36712        \param alpha Standard deviation of the gradient blur.
36713        \param sigma Standard deviation of the structure tensor blur.
36714        \param is_sqrt Tells if the square root of the tensor field is computed instead.
36715     **/
36716     CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
36717                                const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
36718       CImg<Tfloat> res;
36719       const float
36720         nsharpness = std::max(sharpness,1e-5f),
36721         power1 = (is_sqrt?0.5f:1)*nsharpness,
36722         power2 = power1/(1e-7f + 1 - anisotropy);
36723       blur(alpha).normalize(0,(T)255);
36724 
36725       if (_depth>1) { // 3d
36726         get_structure_tensors().move_to(res).blur(sigma);
36727         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=256 && _height*_depth>=256))
36728         cimg_forYZ(*this,y,z) {
36729           Tfloat
36730             *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2),
36731             *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5);
36732           CImg<floatT> val(3), vec(3,3);
36733           cimg_forX(*this,x) {
36734             res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
36735             const float
36736               _l1 = val[2], _l2 = val[1], _l3 = val[0],
36737               l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
36738               ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
36739               vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
36740               wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
36741               n1 = (float)std::pow(1 + l1 + l2 + l3,-power1),
36742               n2 = (float)std::pow(1 + l1 + l2 + l3,-power2);
36743             *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
36744             *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
36745             *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
36746             *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
36747             *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
36748             *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
36749           }
36750         }
36751       } else { // for 2d images
36752         get_structure_tensors().move_to(res).blur(sigma);
36753         cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256 && _height>=256))
36754         cimg_forY(*this,y) {
36755           Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2);
36756           CImg<floatT> val(2), vec(2,2);
36757           cimg_forX(*this,x) {
36758             res.get_tensor_at(x,y).symmetric_eigen(val,vec);
36759             const float
36760               _l1 = val[1], _l2 = val[0],
36761               l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
36762               ux = vec(1,0), uy = vec(1,1),
36763               vx = vec(0,0), vy = vec(0,1),
36764               n1 = (float)std::pow(1 + l1 + l2,-power1),
36765               n2 = (float)std::pow(1 + l1 + l2,-power2);
36766             *(ptrd0++) = n1*ux*ux + n2*vx*vx;
36767             *(ptrd1++) = n1*ux*uy + n2*vx*vy;
36768             *(ptrd2++) = n1*uy*uy + n2*vy*vy;
36769           }
36770         }
36771       }
36772       return res.move_to(*this);
36773     }
36774 
36775     //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance.
36776     CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
36777                                        const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
36778       return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
36779     }
36780 
36781     //! Estimate displacement field between two images.
36782     /**
36783        \param source Reference image.
36784        \param smoothness Smoothness of estimated displacement field.
36785        \param precision Precision required for algorithm convergence.
36786        \param nb_scales Number of scales used to estimate the displacement field.
36787        \param iteration_max Maximum number of iterations allowed for one scale.
36788        \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)).
36789        \param guide Image used as the initial correspondence estimate for the algorithm.
36790        'guide' may have a last channel with boolean values (0=false | other=true) that
36791        tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
36792     **/
36793     CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.0f,
36794                           const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
36795                           const bool is_backward=false,
36796                           const CImg<floatT>& guide=CImg<floatT>::const_empty()) {
36797       return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide).
36798         move_to(*this);
36799     }
36800 
36801     //! Estimate displacement field between two images \newinstance.
36802     CImg<floatT> get_displacement(const CImg<T>& source,
36803                                   const float smoothness=0.1f, const float precision=5.0f,
36804                                   const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
36805                                   const bool is_backward=false,
36806                                   const CImg<floatT>& guide=CImg<floatT>::const_empty()) const {
36807       if (is_empty() || !source) return +*this;
36808       if (!is_sameXYZC(source))
36809         throw CImgArgumentException(_cimg_instance
36810                                     "displacement(): Instance and source image (%u,%u,%u,%u,%p) have "
36811                                     "different dimensions.",
36812                                     cimg_instance,
36813                                     source._width,source._height,source._depth,source._spectrum,source._data);
36814       if (precision<0)
36815         throw CImgArgumentException(_cimg_instance
36816                                     "displacement(): Invalid specified precision %g "
36817                                     "(should be >=0)",
36818                                     cimg_instance,
36819                                     precision);
36820 
36821       const bool is_3d = source._depth>1;
36822       const unsigned int constraint = is_3d?3:2;
36823 
36824       if (guide &&
36825           (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<constraint))
36826         throw CImgArgumentException(_cimg_instance
36827                                     "displacement(): Specified guide (%u,%u,%u,%u,%p) "
36828                                     "has invalid dimensions.",
36829                                     cimg_instance,
36830                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
36831 
36832       const unsigned int
36833         mins = is_3d?cimg::min(_width,_height,_depth):std::min(_width,_height),
36834         _nb_scales = nb_scales>0?nb_scales:
36835         (unsigned int)cimg::round(std::log(mins/8.0)/std::log(1.5),1,1);
36836 
36837       const float _precision = (float)std::pow(10.0,-(double)precision);
36838       float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
36839       const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
36840 
36841       CImg<floatT> U, V;
36842       floatT bound = 0;
36843       for (int scale = (int)_nb_scales - 1; scale>=0; --scale) {
36844         const float factor = (float)std::pow(1.5,(double)scale);
36845         const unsigned int
36846           _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
36847           _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
36848           _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
36849         if (sw<5 && sh<5 && (!is_3d || sd<5)) continue;  // skip too small scales.
36850         const CImg<Tfloat>
36851           I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
36852           I2 = (get_resize(I1,2)-=tm)/=tdelta;
36853         if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V);
36854         if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
36855         else {
36856           if (guide)
36857             guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U);
36858           else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
36859         }
36860 
36861         float dt = 2, energy = cimg::type<float>::max();
36862         const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
36863         cimg_abort_init;
36864 
36865         for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
36866           cimg_abort_test;
36867           float _energy = 0;
36868 
36869           if (is_3d) { // 3d version.
36870             if (smoothness>=0) // Isotropic regularization.
36871               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height*_depth>=8 && _width>=16)
36872                                  reduction(+:_energy))
36873               cimg_forYZ(U,y,z) {
36874                 const int
36875                   _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
36876                   _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
36877                 cimg_for3X(U,x) {
36878                   const float
36879                     X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
36880                     Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
36881                     Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
36882                   float delta_I = 0, _energy_regul = 0;
36883                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
36884                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
36885                   cimg_forC(U,c) {
36886                     const float
36887                       Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
36888                       Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
36889                       Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
36890                       Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
36891                       Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
36892                       Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
36893                     U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
36894                                                           smoothness* ( Uxx + Uyy + Uzz)))/(1 + 6*smoothness*dt);
36895                     _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
36896                   }
36897                   if (is_backward) { // Constraint displacement vectors to stay in image.
36898                     if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
36899                     if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
36900                     if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
36901                     bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
36902                     bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
36903                     bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
36904                   } else {
36905                     if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
36906                     if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
36907                     if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
36908                     bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
36909                     bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
36910                     bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
36911                   }
36912                   _energy+=delta_I*delta_I + smoothness*_energy_regul;
36913                 }
36914                 if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints.
36915                     U(x,y,z,0) = V(x,y,z,0)/factor;
36916                     U(x,y,z,1) = V(x,y,z,1)/factor;
36917                     U(x,y,z,2) = V(x,y,z,2)/factor;
36918                   }
36919               } else { // Anisotropic regularization.
36920               const float nsmoothness = -smoothness;
36921               cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height*_depth>=8 && _width>=16)
36922                                  reduction(+:_energy))
36923               cimg_forYZ(U,y,z) {
36924                 const int
36925                   _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
36926                   _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
36927                 cimg_for3X(U,x) {
36928                   const float
36929                     X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
36930                     Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
36931                     Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
36932                   float delta_I = 0, _energy_regul = 0;
36933                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
36934                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
36935                   cimg_forC(U,c) {
36936                     const float
36937                       Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
36938                       Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
36939                       Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
36940                       N2 = Ux*Ux + Uy*Uy + Uz*Uz,
36941                       N = std::sqrt(N2),
36942                       N3 = 1e-5f + N2*N,
36943                       coef_a = (1 - Ux*Ux/N2)/N,
36944                       coef_b = -2*Ux*Uy/N3,
36945                       coef_c = -2*Ux*Uz/N3,
36946                       coef_d = (1 - Uy*Uy/N2)/N,
36947                       coef_e = -2*Uy*Uz/N3,
36948                       coef_f = (1 - Uz*Uz/N2)/N,
36949                       Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
36950                       Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
36951                       Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
36952                       Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
36953                       Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
36954                       Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
36955                     U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
36956                                                           nsmoothness* ( coef_a*Uxx + coef_b*Uxy +
36957                                                                          coef_c*Uxz + coef_d*Uyy +
36958                                                                          coef_e*Uyz + coef_f*Uzz ))
36959                                          )/(1 + 2*(coef_a + coef_d + coef_f)*nsmoothness*dt);
36960                     _energy_regul+=N;
36961                   }
36962                   if (is_backward) { // Constraint displacement vectors to stay in image.
36963                     if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
36964                     if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
36965                     if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
36966                     bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
36967                     bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
36968                     bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
36969                   } else {
36970                     if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
36971                     if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
36972                     if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
36973                     bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
36974                     bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
36975                     bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
36976                   }
36977                   _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
36978                 }
36979                 if (V) cimg_forXYZ(V,x,y,z) if (V(x,y,z,3)) { // Apply constraints.
36980                     U(x,y,z,0) = V(x,y,z,0)/factor;
36981                     U(x,y,z,1) = V(x,y,z,1)/factor;
36982                     U(x,y,z,2) = V(x,y,z,2)/factor;
36983                   }
36984               }
36985             }
36986           } else { // 2d version.
36987             if (smoothness>=0) // Isotropic regularization.
36988               cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy))
36989               cimg_forY(U,y) {
36990                 const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
36991                 cimg_for3X(U,x) {
36992                   const float
36993                     X = is_backward?x - U(x,y,0):x + U(x,y,0),
36994                     Y = is_backward?y - U(x,y,1):y + U(x,y,1);
36995                   float delta_I = 0, _energy_regul = 0;
36996                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
36997                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
36998                   cimg_forC(U,c) {
36999                     const float
37000                       Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
37001                       Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
37002                       Uxx = U(_n1x,y,c) + U(_p1x,y,c),
37003                       Uyy = U(x,_n1y,c) + U(x,_p1y,c);
37004                     U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
37005                                                       smoothness*( Uxx + Uyy )))/(1 + 4*smoothness*dt);
37006                     _energy_regul+=Ux*Ux + Uy*Uy;
37007                   }
37008                   if (is_backward) { // Constraint displacement vectors to stay in image.
37009                     if (U(x,y,0)>x) U(x,y,0) = (float)x;
37010                     if (U(x,y,1)>y) U(x,y,1) = (float)y;
37011                     bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
37012                     bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
37013                   } else {
37014                     if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
37015                     if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
37016                     bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
37017                     bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
37018                   }
37019                   _energy+=delta_I*delta_I + smoothness*_energy_regul;
37020                 }
37021                 if (V) cimg_forX(V,x) if (V(x,y,2)) { // Apply constraints.
37022                     U(x,y,0) = V(x,y,0)/factor;
37023                     U(x,y,1) = V(x,y,1)/factor;
37024                   }
37025               } else { // Anisotropic regularization.
37026               const float nsmoothness = -smoothness;
37027               cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=8 && _width>=16) reduction(+:_energy))
37028               cimg_forY(U,y) {
37029                 const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
37030                 cimg_for3X(U,x) {
37031                   const float
37032                     X = is_backward?x - U(x,y,0):x + U(x,y,0),
37033                     Y = is_backward?y - U(x,y,1):y + U(x,y,1);
37034                   float delta_I = 0, _energy_regul = 0;
37035                   if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
37036                   else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
37037                   cimg_forC(U,c) {
37038                     const float
37039                       Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
37040                       Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
37041                       N2 = Ux*Ux + Uy*Uy,
37042                       N = std::sqrt(N2),
37043                       N3 = 1e-5f + N2*N,
37044                       coef_a = Uy*Uy/N3,
37045                       coef_b = -2*Ux*Uy/N3,
37046                       coef_c = Ux*Ux/N3,
37047                       Uxx = U(_n1x,y,c) + U(_p1x,y,c),
37048                       Uyy = U(x,_n1y,c) + U(x,_p1y,c),
37049                       Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
37050                     U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
37051                                                       nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/
37052                       (1 + 2*(coef_a + coef_c)*nsmoothness*dt);
37053                     _energy_regul+=N;
37054                   }
37055                   if (is_backward) { // Constraint displacement vectors to stay in image.
37056                     if (U(x,y,0)>x) U(x,y,0) = (float)x;
37057                     if (U(x,y,1)>y) U(x,y,1) = (float)y;
37058                     bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
37059                     bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
37060                   } else {
37061                     if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
37062                     if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
37063                     bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
37064                     bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
37065                   }
37066                   _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
37067                 }
37068                 if (V) cimg_forX(V,x) if (V(x,y,2)) { // Apply constraints.
37069                     U(x,y,0) = V(x,y,0)/factor;
37070                     U(x,y,1) = V(x,y,1)/factor;
37071                   }
37072               }
37073             }
37074           }
37075           const float d_energy = (_energy - energy)/(sw*sh*sd);
37076           if (d_energy<=0 && -d_energy<_precision) break;
37077           if (d_energy>0) dt*=0.5f;
37078           energy = _energy;
37079         }
37080       }
37081       return U;
37082     }
37083 
37084     //! Compute correspondence map between two images, using the patch-match algorithm.
37085     /**
37086         \param patch_image The image containing the reference patches to match with the instance image.
37087         \param patch_width Width of the patch used for matching.
37088         \param patch_height Height of the patch used for matching.
37089         \param patch_depth Depth of the patch used for matching.
37090         \param nb_iterations Number of patch-match iterations.
37091         \param nb_randoms Number of randomization attempts (per pixel).
37092         \param guide Image used as the initial correspondence estimate for the algorithm.
37093           'guide' may have a last channel with boolean values (0=false | other=true) that
37094           tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
37095         \param[out] matching_score Returned as the image of matching scores.
37096         \note
37097         The patch-match algorithm is described in this paper:
37098         Connelly Barnes, Eli Shechtman, Adam Finkelstein, Dan B Goldman(2009),
37099         PatchMatch: A Randomized Correspondence Algorithm for Structural Image Editing
37100     **/
37101     template<typename t1, typename t2>
37102     CImg<T>& patchmatch(const CImg<T>& patch_image,
37103                         const unsigned int patch_width,
37104                         const unsigned int patch_height,
37105                         const unsigned int patch_depth,
37106                         const unsigned int nb_iterations,
37107                         const unsigned int nb_randoms,
37108                         const CImg<t1> &guide,
37109                         CImg<t2> &matching_score) {
37110       return get_patchmatch(patch_image,patch_width,patch_height,patch_depth,
37111                             nb_iterations,nb_randoms,guide,matching_score).move_to(*this);
37112     }
37113 
37114     //! Compute correspondence map between two images, using the patch-match algorithm \newinstance.
37115     template<typename t1, typename t2>
37116     CImg<intT> get_patchmatch(const CImg<T>& patch_image,
37117                               const unsigned int patch_width,
37118                               const unsigned int patch_height,
37119                               const unsigned int patch_depth,
37120                               const unsigned int nb_iterations,
37121                               const unsigned int nb_randoms,
37122                               const CImg<t1> &guide,
37123                               CImg<t2> &matching_score) const {
37124       return _patchmatch(patch_image,patch_width,patch_height,patch_depth,
37125                          nb_iterations,nb_randoms,
37126                          guide,true,matching_score);
37127     }
37128 
37129     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
37130     template<typename t>
37131     CImg<T>& patchmatch(const CImg<T>& patch_image,
37132                         const unsigned int patch_width,
37133                         const unsigned int patch_height,
37134                         const unsigned int patch_depth,
37135                         const unsigned int nb_iterations,
37136                         const unsigned int nb_randoms,
37137                         const CImg<t> &guide) {
37138       return get_patchmatch(patch_image,patch_width,patch_height,patch_depth,
37139                             nb_iterations,nb_randoms,guide).move_to(*this);
37140     }
37141 
37142     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
37143     template<typename t>
37144     CImg<intT> get_patchmatch(const CImg<T>& patch_image,
37145                               const unsigned int patch_width,
37146                               const unsigned int patch_height,
37147                               const unsigned int patch_depth,
37148                               const unsigned int nb_iterations,
37149                               const unsigned int nb_randoms,
37150                               const CImg<t> &guide) const {
37151       return _patchmatch(patch_image,patch_width,patch_height,patch_depth,
37152                          nb_iterations,nb_randoms,
37153                          guide,false,CImg<T>::empty());
37154     }
37155 
37156     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
37157     CImg<T>& patchmatch(const CImg<T>& patch_image,
37158                         const unsigned int patch_width,
37159                         const unsigned int patch_height,
37160                         const unsigned int patch_depth=1,
37161                         const unsigned int nb_iterations=5,
37162                         const unsigned int nb_randoms=5) {
37163       return get_patchmatch(patch_image,patch_width,patch_height,patch_depth,
37164                             nb_iterations,nb_randoms).move_to(*this);
37165     }
37166 
37167     //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
37168     CImg<intT> get_patchmatch(const CImg<T>& patch_image,
37169                               const unsigned int patch_width,
37170                               const unsigned int patch_height,
37171                               const unsigned int patch_depth=1,
37172                               const unsigned int nb_iterations=5,
37173                               const unsigned int nb_randoms=5) const {
37174       return _patchmatch(patch_image,patch_width,patch_height,patch_depth,
37175                          nb_iterations,nb_randoms,
37176                          CImg<T>::const_empty(),
37177                          false,CImg<T>::empty());
37178     }
37179 
37180     template<typename t1, typename t2>
37181     CImg<intT> _patchmatch(const CImg<T>& patch_image,
37182                            const unsigned int patch_width,
37183                            const unsigned int patch_height,
37184                            const unsigned int patch_depth,
37185                            const unsigned int nb_iterations,
37186                            const unsigned int nb_randoms,
37187                            const CImg<t1> &guide,
37188                            const bool is_matching_score,
37189                            CImg<t2> &matching_score) const {
37190       if (is_empty()) return CImg<intT>::const_empty();
37191       if (patch_image._spectrum!=_spectrum)
37192         throw CImgArgumentException(_cimg_instance
37193                                     "patchmatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) "
37194                                     "have different spectrums.",
37195                                     cimg_instance,
37196                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
37197                                     patch_image._data);
37198       if (patch_width>_width || patch_height>_height || patch_depth>_depth)
37199         throw CImgArgumentException(_cimg_instance
37200                                     "patchmatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
37201                                     "of the instance image.",
37202                                     cimg_instance,patch_width,patch_height,patch_depth);
37203       if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth)
37204         throw CImgArgumentException(_cimg_instance
37205                                     "patchmatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
37206                                     "of the patch image image (%u,%u,%u,%u,%p).",
37207                                     cimg_instance,patch_width,patch_height,patch_depth,
37208                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
37209                                     patch_image._data);
37210       const unsigned int
37211         _constraint = patch_image._depth>1?3:2,
37212         constraint = guide._spectrum>_constraint?_constraint:0;
37213 
37214       if (guide &&
37215           (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint))
37216         throw CImgArgumentException(_cimg_instance
37217                                     "patchmatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions "
37218                                     "considering instance and patch image image (%u,%u,%u,%u,%p).",
37219                                     cimg_instance,
37220                                     guide._width,guide._height,guide._depth,guide._spectrum,guide._data,
37221                                     patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
37222                                     patch_image._data);
37223 
37224       CImg<intT> map(_width,_height,_depth,patch_image._depth>1?3:2);
37225       CImg<floatT> score(_width,_height,_depth);
37226       const int
37227         psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1,
37228         psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1,
37229         psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1;
37230 
37231       if (_depth>1 || patch_image._depth>1) { // 3d version.
37232 
37233         // Initialize correspondence map.
37234         if (guide) cimg_forXYZ(*this,x,y,z) { // User-defined initialization.
37235             const int
37236               cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
37237               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
37238               cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
37239               u = std::min(std::max((int)guide(x,y,z,0),cx1),patch_image.width() - 1 - cx2),
37240               v = std::min(std::max((int)guide(x,y,z,1),cy1),patch_image.height() - 1 - cy2),
37241               w = std::min(std::max((int)guide(x,y,z,2),cz1),patch_image.depth() - 1 - cz2);
37242             map(x,y,z,0) = u;
37243             map(x,y,z,1) = v;
37244             map(x,y,z,2) = w;
37245             score(x,y,z) = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
37246                                        x - cx1,y - cy1,z - cz1,
37247                                        u - cx1,v - cy1,w - cz1,cimg::type<float>::inf());
37248           } else cimg_forXYZ(*this,x,y,z) { // Random initialization.
37249             const int
37250               cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
37251               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
37252               cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
37253               u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2)),
37254               v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2)),
37255               w = (int)cimg::round(cimg::rand(cz1,patch_image.depth() - 1 - cz2));
37256             map(x,y,z,0) = u;
37257             map(x,y,z,1) = v;
37258             map(x,y,z,2) = w;
37259             score(x,y,z) = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
37260                                        x - cx1,y - cy1,z - cz1,
37261                                        u - cx1,v - cy1,w - cz1,cimg::type<float>::inf());
37262           }
37263 
37264         // Start iteration loop.
37265         cimg_abort_init;
37266         for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
37267           cimg_abort_test;
37268           const bool is_even = !(iter%2);
37269 
37270           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>64 && iter<nb_iterations-2))
37271           cimg_forXYZ(*this,X,Y,Z) {
37272             const int
37273               x = is_even?X:width() - 1 - X,
37274               y = is_even?Y:height() - 1 - Y,
37275               z = is_even?Z:depth() - 1 - Z;
37276             if (score(x,y,z)<=1e-5 || (constraint && guide(x,y,z,constraint)!=0)) continue;
37277             const int
37278               cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
37279               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
37280               cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
37281               xp = x - cx1,
37282               yp = y - cy1,
37283               zp = z - cz1;
37284 
37285             // Propagation.
37286             if (is_even) {
37287               if (x>0) { // Compare with left neighbor.
37288                 const int u = map(x - 1,y,z,0), v = map(x - 1,y,z,1), w = map(x - 1,y,z,2);
37289                 if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
37290                     v>=cy1 && v<patch_image.height() - cy2 &&
37291                     w>=cz1 && w<patch_image.depth() - cz2) {
37292                   const float
37293                     current_score = score(x,y,z),
37294                     D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
37295                                     xp,yp,zp,u + 1 - cx1,v - cy1,w - cz1,current_score);
37296                   if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u + 1; map(x,y,z,1) = v; map(x,y,z,2) = w; }
37297                 }
37298               }
37299               if (y>0) { // Compare with up neighbor.
37300                 const int u = map(x,y - 1,z,0), v = map(x,y - 1,z,1), w = map(x,y - 1,z,2);
37301                 if (u>=cx1 && u<patch_image.width() - cx2 &&
37302                     v>=cy1 - 1 && v<patch_image.height() - 1 - cy2 &&
37303                     w>=cz1 && w<patch_image.depth() - cx2) {
37304                   const float
37305                     current_score = score(x,y,z),
37306                     D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
37307                                     xp,yp,zp,u - cx1,v + 1 - cy1,w - cz1,current_score);
37308                   if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u; map(x,y,z,1) = v + 1; map(x,y,z,2) = w; }
37309                 }
37310               }
37311               if (z>0) { // Compare with backward neighbor.
37312                 const int u = map(x,y,z - 1,0), v = map(x,y,z - 1,1), w = map(x,y,z - 1,2);
37313                 if (u>=cx1 && u<patch_image.width() - cx2 &&
37314                     v>=cy1 && v<patch_image.height() - cy2 &&
37315                     w>=cz1 - 1 && w<patch_image.depth() - 1 - cz2) {
37316                   const float
37317                     current_score = score(x,y,z),
37318                     D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
37319                                     xp,yp,zp,u - cx1,v - cy1,w + 1 - cz1,current_score);
37320                   if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u; map(x,y,z,1) = v; map(x,y,z,2) = w + 1; }
37321                 }
37322               }
37323             } else {
37324               if (x<width() - 1) { // Compare with right neighbor.
37325                 const int u = map(x + 1,y,z,0), v = map(x + 1,y,z,1), w = map(x + 1,y,z,2);
37326                 if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
37327                     v>=cy1 && v<patch_image.height() - cy2 &&
37328                     w>=cz1 && w<patch_image.depth() - cz2) {
37329                   const float
37330                     current_score = score(x,y,z),
37331                     D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
37332                                     xp,yp,zp,u - 1 - cx1,v - cy1,w - cz1,current_score);
37333                   if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u - 1; map(x,y,z,1) = v; map(x,y,z,2) = w; }
37334                 }
37335               }
37336               if (y<height() - 1) { // Compare with bottom neighbor.
37337                 const int u = map(x,y + 1,z,0), v = map(x,y + 1,z,1), w = map(x,y + 1,z,2);
37338                 if (u>=cx1 && u<patch_image.width() - cx2 &&
37339                     v>=cy1 + 1 && v<patch_image.height() + 1 - cy2 &&
37340                     w>=cz1 && w<patch_image.depth() - cz2) {
37341                   const float
37342                     current_score = score(x,y,z),
37343                     D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
37344                                     xp,yp,zp,u - cx1,v - 1 - cy1,w - cz1,current_score);
37345                   if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u; map(x,y,z,1) = v - 1; map(x,y,z,2) = w; }
37346                 }
37347               }
37348               if (z<depth() - 1) { // Compare with forward neighbor.
37349                 const int u = map(x,y,z + 1,0), v = map(x,y,z + 1,1), w = map(x,y,z + 1,2);
37350                 if (u>=cx1 && u<patch_image.width() - cx2 &&
37351                     v>=cy1 && v<patch_image.height() - cy2 &&
37352                     w>=cz1 + 1 && w<patch_image.depth() + 1 - cz2) {
37353                   const float
37354                     current_score = score(x,y,z),
37355                     D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
37356                                     xp,yp,zp,u - cx1,v - cy1,w - 1 - cz1,current_score);
37357                   if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = u; map(x,y,z,1) = v; map(x,y,z,2) = w - 1; }
37358                 }
37359               }
37360             }
37361 
37362             // Randomization.
37363             const int u = map(x,y,z,0), v = map(x,y,z,1), w = map(x,y,z,2);
37364             float dw = (float)patch_image.width(), dh = (float)patch_image.height(), dd = (float)patch_image.depth();
37365             for (unsigned int i = 0; i<nb_randoms; ++i) {
37366               const int
37367                 ui = (int)cimg::round(cimg::rand(std::max((float)cx1,u - dw),
37368                                                  std::min(patch_image.width() - 1.0f - cx2,u + dw))),
37369                 vi = (int)cimg::round(cimg::rand(std::max((float)cy1,v - dh),
37370                                                  std::min(patch_image.height() - 1.0f - cy2,v + dh))),
37371                 wi = (int)cimg::round(cimg::rand(std::max((float)cz1,w - dd),
37372                                                  std::min(patch_image.depth() - 1.0f - cz2,w + dd)));
37373               if (ui!=u || vi!=v || wi!=w) {
37374                 const float
37375                   current_score = score(x,y,z),
37376                   D = _patchmatch(*this,patch_image,patch_width,patch_height,patch_depth,
37377                                   xp,yp,zp,ui - cx1,vi - cy1,wi - cz1,current_score);
37378                 if (D<current_score) { score(x,y,z) = D; map(x,y,z,0) = ui; map(x,y,z,1) = vi; map(x,y,z,2) = wi; }
37379                 dw = std::max(5.0f,dw*0.5f); dh = std::max(5.0f,dh*0.5f); dd = std::max(5.0f,dd*0.5f);
37380               }
37381             }
37382           }
37383         }
37384 
37385       } else { // 2d version.
37386 
37387         // Initialize correspondence map.
37388         if (guide) cimg_forXY(*this,x,y) { // Random initialization.
37389             const int
37390               cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
37391               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()) , cy2 = psizeh - cy1 - 1,
37392               u = std::min(std::max((int)guide(x,y,0),cx1),patch_image.width() - 1 - cx2),
37393               v = std::min(std::max((int)guide(x,y,1),cy1),patch_image.height() - 1 - cy2);
37394             map(x,y,0) = u;
37395             map(x,y,1) = v;
37396             score(x,y) = _patchmatch(*this,patch_image,patch_width,patch_height,
37397                                      x - cx1,y - cy1,u - cx1,v - cy1,cimg::type<float>::inf());
37398           } else cimg_forXY(*this,x,y) { // Random initialization.
37399             const int
37400               cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
37401               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()) , cy2 = psizeh - cy1 - 1,
37402               u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2)),
37403               v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2));
37404             map(x,y,0) = u;
37405             map(x,y,1) = v;
37406             score(x,y) = _patchmatch(*this,patch_image,patch_width,patch_height,
37407                                      x - cx1,y - cy1,u - cx1,v - cy1,cimg::type<float>::inf());
37408           }
37409 
37410         // Start iteration loop.
37411         for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
37412           const bool is_even = !(iter%2);
37413 
37414           cimg_pragma_openmp(parallel for cimg_openmp_if(_width>64 && iter<nb_iterations-2))
37415           cimg_forXY(*this,X,Y) {
37416             const int
37417               x = is_even?X:width() - 1 - X,
37418               y = is_even?Y:height() - 1 - Y;
37419             if (score(x,y)<=1e-5 || (constraint && guide(x,y,constraint)!=0)) continue;
37420             const int
37421               cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
37422               cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()) , cy2 = psizeh - cy1 - 1,
37423               xp = x - cx1,
37424               yp = y - cy1;
37425 
37426             // Propagation.
37427             if (is_even) {
37428               if (x>0) { // Compare with left neighbor.
37429                 const int u = map(x - 1,y,0), v = map(x - 1,y,1);
37430                 if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
37431                     v>=cy1 && v<patch_image.height() - cy2) {
37432                   const float
37433                     current_score = score(x,y),
37434                     D = _patchmatch(*this,patch_image,patch_width,patch_height,
37435                                     xp,yp,u + 1 - cx1,v - cy1,current_score);
37436                   if (D<current_score) { score(x,y) = D; map(x,y,0) = u + 1; map(x,y,1) = v; }
37437                 }
37438               }
37439               if (y>0) { // Compare with up neighbor.
37440                 const int u = map(x,y - 1,0), v = map(x,y - 1,1);
37441                 if (u>=cx1 && u<patch_image.width() - cx2 &&
37442                     v>=cy1 - 1 && v<patch_image.height() - 1 - cy2) {
37443                   const float
37444                     current_score = score(x,y),
37445                     D = _patchmatch(*this,patch_image,patch_width,patch_height,
37446                                     xp,yp,u - cx1,v + 1 - cy1,current_score);
37447                   if (D<current_score) { score(x,y) = D; map(x,y,0) = u; map(x,y,1) = v + 1; }
37448                 }
37449               }
37450             } else {
37451               if (x<width() - 1) { // Compare with right neighbor.
37452                 const int u = map(x + 1,y,0), v = map(x + 1,y,1);
37453                 if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
37454                     v>=cy1 && v<patch_image.height() - cy2) {
37455                   const float
37456                     current_score = score(x,y),
37457                     D = _patchmatch(*this,patch_image,patch_width,patch_height,
37458                                     xp,yp,u - 1 - cx1,v - cy1,current_score);
37459                   if (D<current_score) { score(x,y) = D; map(x,y,0) = u - 1; map(x,y,1) = v; }
37460                 }
37461               }
37462               if (y<height() - 1) { // Compare with bottom neighbor.
37463                 const int u = map(x,y + 1,0), v = map(x,y + 1,1);
37464                 if (u>=cx1 && u<patch_image.width() - cx2 &&
37465                     v>=cy1 + 1 && v<patch_image.height() + 1 - cy2) {
37466                   const float
37467                     current_score = score(x,y),
37468                     D = _patchmatch(*this,patch_image,patch_width,patch_height,
37469                                     xp,yp,u - cx1,v - 1 - cy1,current_score);
37470                   if (D<current_score) { score(x,y) = D; map(x,y,0) = u; map(x,y,1) = v - 1; }
37471                 }
37472               }
37473             }
37474 
37475             // Randomization.
37476             const int u = map(x,y,0), v = map(x,y,1);
37477             float dw = (float)patch_image.width(), dh = (float)patch_image.height();
37478             for (unsigned int i = 0; i<nb_randoms; ++i) {
37479               const int
37480                 ui = (int)cimg::round(cimg::rand(std::max((float)cx1,u - dw),
37481                                                  std::min(patch_image.width() - 1.0f - cx2,u + dw))),
37482                 vi = (int)cimg::round(cimg::rand(std::max((float)cy1,v - dh),
37483                                                  std::min(patch_image.height() - 1.0f - cy2,v + dh)));
37484               if (ui!=u || vi!=v) {
37485                 const float
37486                   current_score = score(x,y),
37487                   D = _patchmatch(*this,patch_image,patch_width,patch_height,
37488                                   xp,yp,ui - cx1,vi - cy1,current_score);
37489                 if (D<current_score) { score(x,y) = D; map(x,y,0) = ui; map(x,y,1) = vi; }
37490                 dw = std::max(5.0f,dw*0.5f); dh = std::max(5.0f,dh*0.5f);
37491               }
37492             }
37493           }
37494         }
37495       }
37496       if (is_matching_score) score.move_to(matching_score);
37497       return map;
37498     }
37499 
37500     // Compute SSD between two patches in different images.
37501     static float _patchmatch(const CImg<T>& img1, const CImg<T>& img2,
37502                              const unsigned int psizew, const unsigned int psizeh,
37503                              const int x1, const int y1,
37504                              const int x2, const int y2,
37505                              const float max_ssd) { // 2d version.
37506       const T *p1 = img1.data(x1,y1), *p2 = img2.data(x2,y2);
37507       const ulongT
37508         offx1 = (ulongT)img1._width - psizew,
37509         offx2 = (ulongT)img2._width - psizew,
37510         offy1 = (ulongT)img1._width*img1._height - psizeh*img1._width,
37511         offy2 = (ulongT)img2._width*img2._height - psizeh*img2._width;
37512       float ssd = 0;
37513       cimg_forC(img1,c) {
37514         for (unsigned int j = 0; j<psizeh; ++j) {
37515           for (unsigned int i = 0; i<psizew; ++i)
37516             ssd += cimg::sqr(*(p1++) - *(p2++));
37517           if (ssd>max_ssd) return max_ssd;
37518           p1+=offx1; p2+=offx2;
37519         }
37520         p1+=offy1; p2+=offy2;
37521       }
37522       return ssd;
37523     }
37524 
37525     static float _patchmatch(const CImg<T>& img1, const CImg<T>& img2,
37526                              const unsigned int psizew, const unsigned int psizeh, const unsigned int psized,
37527                              const int x1, const int y1, const int z1,
37528                              const int x2, const int y2, const int z2,
37529                              const float max_ssd) { // 3d version.
37530       const T *p1 = img1.data(x1,y1,z1), *p2 = img2.data(x2,y2,z2);
37531       const ulongT
37532         offx1 = (ulongT)img1._width - psizew,
37533         offx2 = (ulongT)img2._width - psizew,
37534         offy1 = (ulongT)img1._width*img1._height - psizeh*img1._width - psizew,
37535         offy2 = (ulongT)img2._width*img2._height - psizeh*img2._width - psizew,
37536         offz1 = (ulongT)img1._width*img1._height*img1._depth - psized*img1._width*img1._height -
37537         psizeh*img1._width - psizew,
37538         offz2 = (ulongT)img2._width*img2._height*img2._depth - psized*img2._width*img2._height -
37539         psizeh*img2._width - psizew;
37540       float ssd = 0;
37541       cimg_forC(img1,c) {
37542         for (unsigned int k = 0; k<psized; ++k) {
37543           for (unsigned int j = 0; j<psizeh; ++j) {
37544             for (unsigned int i = 0; i<psizew; ++i)
37545               ssd += cimg::sqr(*(p1++) - *(p2++));
37546             if (ssd>max_ssd) return max_ssd;
37547             p1+=offx1; p2+=offx2;
37548           }
37549           p1+=offy1; p2+=offy2;
37550         }
37551         p1+=offz1; p2+=offz2;
37552       }
37553       return ssd;
37554     }
37555 
37556     //! Compute Euclidean distance function to a specified value.
37557     /**
37558         \param value Reference value.
37559         \param metric Type of metric. Can be <tt>{ 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }</tt>.
37560         \note
37561         The distance transform implementation has been submitted by A. Meijster, and implements
37562         the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink,
37563                      "A general algorithm for computing distance transforms in linear time.",
37564                      In: Mathematical Morphology and its Applications to Image and Signal Processing,
37565                      J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.'
37566          The submitted code has then been modified to fit CImg coding style and constraints.
37567     **/
37568     CImg<T>& distance(const T& value, const unsigned int metric=2) {
37569       if (is_empty()) return *this;
37570       if (cimg::type<Tint>::string()!=cimg::type<T>::string()) // For datatype < int.
37571         return CImg<Tint>(*this,false).distance((Tint)value,metric).
37572           cut((Tint)cimg::type<T>::min(),(Tint)cimg::type<T>::max()).move_to(*this);
37573       bool is_value = false;
37574       cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning)
37575       if (!is_value) return fill(cimg::type<T>::max());
37576       switch (metric) {
37577       case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt);          // Chebyshev.
37578       case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt);          // Manhattan.
37579       case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt);          // Squared Euclidean.
37580       default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt();  // Euclidean.
37581       }
37582       return *this;
37583     }
37584 
37585     //! Compute distance to a specified value \newinstance.
37586     CImg<Tfloat> get_distance(const T& value, const unsigned int metric=2) const {
37587       return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
37588     }
37589 
37590     static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) {
37591       return (u*u - i*i + g[u] - g[i])/(2*(u - i));
37592     }
37593 
37594     static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) {
37595       return (x - i)*(x - i) + g[i];
37596     }
37597 
37598     static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) {
37599       return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2);
37600     }
37601 
37602     static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) {
37603       return (x<i?i - x:x - i) + g[i];
37604     }
37605 
37606     static longT _distance_sep_cdt(const longT i, const longT u, const longT *const g) {
37607       const longT h = (i + u)/2;
37608       if (g[i]<=g[u]) { return h<i + g[u]?i + g[u]:h; }
37609       return h<u - g[i]?h:u - g[i];
37610     }
37611 
37612     static longT _distance_dist_cdt(const longT x, const longT i, const longT *const g) {
37613       const longT d = x<i?i - x:x - i;
37614       return d<g[i]?g[i]:d;
37615     }
37616 
37617     static void _distance_scan(const unsigned int len,
37618                                const longT *const g,
37619                                longT (*const sep)(const longT, const longT, const longT *const),
37620                                longT (*const f)(const longT, const longT, const longT *const),
37621                                longT *const s,
37622                                longT *const t,
37623                                longT *const dt) {
37624       longT q = s[0] = t[0] = 0;
37625       for (int u = 1; u<(int)len; ++u) { // Forward scan.
37626         while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
37627         if (q<0) { q = 0; s[0] = u; }
37628         else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }}
37629       }
37630       for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan.
37631     }
37632 
37633     CImg<T>& _distance_core(longT (*const sep)(const longT, const longT, const longT *const),
37634                             longT (*const f)(const longT, const longT, const longT *const)) {
37635  // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why.
37636 #define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9)
37637 
37638       const ulongT wh = (ulongT)_width*_height;
37639 #if defined(cimg_use_openmp) && !cimg_is_gcc49x
37640       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
37641 #endif
37642       cimg_forC(*this,c) {
37643         CImg<longT> g(_width), dt(_width), s(_width), t(_width);
37644         CImg<T> img = get_shared_channel(c);
37645 #if defined(cimg_use_openmp) && !cimg_is_gcc49x
37646         cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_width>=512 && _height*_depth>=16)
37647                            firstprivate(g,dt,s,t))
37648 #endif
37649         cimg_forYZ(*this,y,z) { // Over X-direction.
37650           cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh);
37651           _distance_scan(_width,g,sep,f,s,t,dt);
37652           cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
37653         }
37654         if (_height>1) {
37655           g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
37656 #if defined(cimg_use_openmp) && !cimg_is_gcc49x
37657           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_height>=512 && _width*_depth>=16)
37658                              firstprivate(g,dt,s,t))
37659 #endif
37660           cimg_forXZ(*this,x,z) { // Over Y-direction.
37661             cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh);
37662             _distance_scan(_height,g,sep,f,s,t,dt);
37663             cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
37664           }
37665         }
37666         if (_depth>1) {
37667           g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
37668 #if defined(cimg_use_openmp) && !cimg_is_gcc49x
37669           cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if(_depth>=512 && _width*_height>=16)
37670                              firstprivate(g,dt,s,t))
37671 #endif
37672           cimg_forXY(*this,x,y) { // Over Z-direction.
37673             cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh);
37674             _distance_scan(_depth,g,sep,f,s,t,dt);
37675             cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
37676           }
37677         }
37678       }
37679       return *this;
37680     }
37681 
37682     //! Compute chamfer distance to a specified value, with a custom metric.
37683     /**
37684        \param value Reference value.
37685        \param metric_mask Metric mask.
37686        \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé.
37687     **/
37688     template<typename t>
37689     CImg<T>& distance(const T& value, const CImg<t>& metric_mask) {
37690       if (is_empty()) return *this;
37691       bool is_value = false;
37692       cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999;
37693       if (!is_value) return fill(cimg::type<T>::max());
37694       const ulongT wh = (ulongT)_width*_height;
37695       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
37696       cimg_forC(*this,c) {
37697         CImg<T> img = get_shared_channel(c);
37698         cimg_pragma_openmp(parallel for collapse(3) cimg_openmp_if(_width*_height*_depth>=1024))
37699         cimg_forXYZ(metric_mask,dx,dy,dz) {
37700           const t weight = metric_mask(dx,dy,dz);
37701           if (weight) {
37702             for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan.
37703               for (int y = dy , ny = 0; y<height(); ++y,++ny) {
37704                 for (int x = dx, nx = 0; x<width(); ++x,++nx) {
37705                   const T dd = img(nx,ny,nz,0,wh) + weight;
37706                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
37707                 }
37708               }
37709             }
37710             for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan.
37711               for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
37712                 for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
37713                   const T dd = img(nx,ny,nz,0,wh) + weight;
37714                   if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
37715                 }
37716               }
37717             }
37718           }
37719         }
37720       }
37721       return *this;
37722     }
37723 
37724     //! Compute chamfer distance to a specified value, with a custom metric \newinstance.
37725     template<typename t>
37726     CImg<Tfloat> get_distance(const T& value, const CImg<t>& metric_mask) const {
37727       return CImg<Tfloat>(*this,false).distance(value,metric_mask);
37728     }
37729 
37730     //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm).
37731     /**
37732        \param value Reference value.
37733        \param metric Field of distance potentials.
37734        \param is_high_connectivity Tells if the algorithm uses low or high connectivity.
37735        \param[out] return_path An image containing the nodes of the minimal path.
37736      **/
37737     template<typename t, typename to>
37738     CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
37739                                CImg<to>& return_path) {
37740       return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this);
37741     }
37742 
37743     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance.
37744     template<typename t, typename to>
37745     CImg<typename cimg::superset<t,long>::type>
37746     get_distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
37747                           CImg<to>& return_path) const {
37748       if (is_empty()) return return_path.assign();
37749       if (!is_sameXYZ(metric))
37750         throw CImgArgumentException(_cimg_instance
37751                                     "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) "
37752                                     "have incompatible dimensions.",
37753                                     cimg_instance,
37754                                     metric._width,metric._height,metric._depth,metric._spectrum);
37755       typedef typename cimg::superset<t,long>::type td;  // Type used for computing cumulative distances.
37756       CImg<td> result(_width,_height,_depth,_spectrum), Q;
37757       CImg<boolT> is_queued(_width,_height,_depth,1);
37758       if (return_path) return_path.assign(_width,_height,_depth,_spectrum);
37759 
37760       cimg_forC(*this,c) {
37761         const CImg<T> img = get_shared_channel(c);
37762         const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
37763         CImg<td> res = result.get_shared_channel(c);
37764         CImg<to> path = return_path?return_path.get_shared_channel(c):CImg<to>();
37765         unsigned int sizeQ = 0;
37766 
37767         // Detect initial seeds.
37768         is_queued.fill(0);
37769         cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) {
37770           Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z);
37771           res(x,y,z) = 0;
37772           if (path) path(x,y,z) = (to)0;
37773         }
37774 
37775         // Start distance propagation.
37776         while (sizeQ) {
37777 
37778           // Get and remove point with minimal potential from the queue.
37779           const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
37780           const td P = (td)-Q(0,0);
37781           Q._priority_queue_remove(sizeQ);
37782 
37783           // Update neighbors.
37784           td npot = 0;
37785           if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) {
37786             res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2;
37787           }
37788           if (x + 1<width() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x + 1,y,z) + P),x + 1,y,z)) {
37789             res(x + 1,y,z) = npot; if (path) path(x + 1,y,z) = (to)1;
37790           }
37791           if (y - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) {
37792             res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8;
37793           }
37794           if (y + 1<height() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y + 1,z) + P),x,y + 1,z)) {
37795             res(x,y + 1,z) = npot; if (path) path(x,y + 1,z) = (to)4;
37796           }
37797           if (z - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) {
37798             res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32;
37799           }
37800           if (z + 1<depth() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z + 1) + P),x,y,z + 1)) {
37801             res(x,y,z + 1) = npot; if (path) path(x,y,z + 1) = (to)16;
37802           }
37803 
37804           if (is_high_connectivity) {
37805             const float sqrt2 = std::sqrt(2.0f), sqrt3 = std::sqrt(3.0f);
37806 
37807             // Diagonal neighbors on slice z.
37808             if (x - 1>=0 && y - 1>=0 &&
37809                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) {
37810               res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10;
37811             }
37812             if (x + 1<width() && y - 1>=0 &&
37813                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) {
37814               res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9;
37815             }
37816             if (x - 1>=0 && y + 1<height() &&
37817                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y + 1,z) + P)),x - 1,y + 1,z)) {
37818               res(x - 1,y + 1,z) = npot; if (path) path(x - 1,y + 1,z) = (to)6;
37819             }
37820             if (x + 1<width() && y + 1<height() &&
37821                 Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y + 1,z) + P)),x + 1,y + 1,z)) {
37822               res(x + 1,y + 1,z) = npot; if (path) path(x + 1,y + 1,z) = (to)5;
37823             }
37824 
37825             if (z - 1>=0) { // Diagonal neighbors on slice z - 1.
37826               if (x - 1>=0 &&
37827                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) {
37828                 res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34;
37829               }
37830               if (x + 1<width() &&
37831                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z - 1) + P)),x + 1,y,z - 1)) {
37832                 res(x + 1,y,z - 1) = npot; if (path) path(x + 1,y,z - 1) = (to)33;
37833               }
37834               if (y - 1>=0 &&
37835                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) {
37836                 res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40;
37837               }
37838               if (y + 1<height() &&
37839                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z - 1) + P)),x,y + 1,z - 1)) {
37840                 res(x,y + 1,z - 1) = npot; if (path) path(x,y + 1,z - 1) = (to)36;
37841               }
37842               if (x - 1>=0 && y - 1>=0 &&
37843                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)),
37844                                            x - 1,y - 1,z - 1)) {
37845                 res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42;
37846               }
37847               if (x + 1<width() && y - 1>=0 &&
37848                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)),
37849                                            x + 1,y - 1,z - 1)) {
37850                 res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41;
37851               }
37852               if (x - 1>=0 && y + 1<height() &&
37853                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z - 1) + P)),
37854                                            x - 1,y + 1,z - 1)) {
37855                 res(x - 1,y + 1,z - 1) = npot; if (path) path(x - 1,y + 1,z - 1) = (to)38;
37856               }
37857               if (x + 1<width() && y + 1<height() &&
37858                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z - 1) + P)),
37859                                            x + 1,y + 1,z - 1)) {
37860                 res(x + 1,y + 1,z - 1) = npot; if (path) path(x + 1,y + 1,z - 1) = (to)37;
37861               }
37862             }
37863 
37864             if (z + 1<depth()) { // Diagonal neighbors on slice z + 1.
37865               if (x - 1>=0 &&
37866                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) {
37867                 res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18;
37868               }
37869               if (x + 1<width() &&
37870                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z + 1) + P)),x + 1,y,z + 1)) {
37871                 res(x + 1,y,z + 1) = npot; if (path) path(x + 1,y,z + 1) = (to)17;
37872               }
37873               if (y - 1>=0 &&
37874                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) {
37875                 res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24;
37876               }
37877               if (y + 1<height() &&
37878                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z + 1) + P)),x,y + 1,z + 1)) {
37879                 res(x,y + 1,z + 1) = npot; if (path) path(x,y + 1,z + 1) = (to)20;
37880               }
37881               if (x - 1>=0 && y - 1>=0 &&
37882                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)),
37883                                            x - 1,y - 1,z + 1)) {
37884                 res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26;
37885               }
37886               if (x + 1<width() && y - 1>=0 &&
37887                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)),
37888                                            x + 1,y - 1,z + 1)) {
37889                 res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25;
37890               }
37891               if (x - 1>=0 && y + 1<height() &&
37892                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z + 1) + P)),
37893                                            x - 1,y + 1,z + 1)) {
37894                 res(x - 1,y + 1,z + 1) = npot; if (path) path(x - 1,y + 1,z + 1) = (to)22;
37895               }
37896               if (x + 1<width() && y + 1<height() &&
37897                   Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z + 1) + P)),
37898                                            x + 1,y + 1,z + 1)) {
37899                 res(x + 1,y + 1,z + 1) = npot; if (path) path(x + 1,y + 1,z + 1) = (to)21;
37900               }
37901             }
37902           }
37903         }
37904       }
37905       return result;
37906     }
37907 
37908     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \overloading.
37909     template<typename t>
37910     CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric,
37911                                const bool is_high_connectivity=false) {
37912       return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this);
37913     }
37914 
37915     //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance.
37916     template<typename t>
37917     CImg<Tfloat> get_distance_dijkstra(const T& value, const CImg<t>& metric,
37918                                        const bool is_high_connectivity=false) const {
37919       CImg<T> return_path;
37920       return get_distance_dijkstra(value,metric,is_high_connectivity,return_path);
37921     }
37922 
37923     //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
37924     /**
37925        \param value Reference value.
37926        \param metric Field of distance potentials.
37927      **/
37928     template<typename t>
37929     CImg<T>& distance_eikonal(const T& value, const CImg<t>& metric) {
37930       return get_distance_eikonal(value,metric).move_to(*this);
37931     }
37932 
37933     //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
37934     template<typename t>
37935     CImg<Tfloat> get_distance_eikonal(const T& value, const CImg<t>& metric) const {
37936       if (is_empty()) return *this;
37937       if (!is_sameXYZ(metric))
37938         throw CImgArgumentException(_cimg_instance
37939                                     "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have "
37940                                     "incompatible dimensions.",
37941                                     cimg_instance,
37942                                     metric._width,metric._height,metric._depth,metric._spectrum);
37943       CImg<Tfloat> result(_width,_height,_depth,_spectrum,cimg::type<Tfloat>::max()), Q;
37944       CImg<charT> state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen.
37945 
37946       cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state))
37947       cimg_forC(*this,c) {
37948         const CImg<T> img = get_shared_channel(c);
37949         const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
37950         CImg<Tfloat> res = result.get_shared_channel(c);
37951         unsigned int sizeQ = 0;
37952         state.fill(-1);
37953 
37954         // Detect initial seeds.
37955         Tfloat *ptr1 = res._data; char *ptr2 = state._data;
37956         cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; }
37957 
37958         // Initialize seeds neighbors.
37959         ptr2 = state._data;
37960         cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) {
37961           if (x - 1>=0 && state(x - 1,y,z)==-1) {
37962             const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
37963             Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
37964           }
37965           if (x + 1<width() && state(x + 1,y,z)==-1) {
37966             const Tfloat dist = res(x + 1,y,z) = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
37967             Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
37968           }
37969           if (y - 1>=0 && state(x,y - 1,z)==-1) {
37970             const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
37971             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
37972           }
37973           if (y + 1<height() && state(x,y + 1,z)==-1) {
37974             const Tfloat dist = res(x,y + 1,z) = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
37975             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
37976           }
37977           if (z - 1>=0 && state(x,y,z - 1)==-1) {
37978             const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
37979             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
37980           }
37981           if (z + 1<depth() && state(x,y,z + 1)==-1) {
37982             const Tfloat dist = res(x,y,z + 1) = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
37983             Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
37984           }
37985         }
37986 
37987         // Propagate front.
37988         while (sizeQ) {
37989           int x = -1, y = -1, z = -1;
37990           while (sizeQ && x<0) {
37991             x = (int)Q(0,1); y = (int)Q(0,2); z = (int)Q(0,3);
37992             Q._priority_queue_remove(sizeQ);
37993             if (state(x,y,z)==1) x = -1; else state(x,y,z) = 1;
37994           }
37995           if (x>=0) {
37996             if (x - 1>=0 && state(x - 1,y,z)!=1) {
37997               const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
37998               if (dist<res(x - 1,y,z)) {
37999                 res(x - 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
38000               }
38001             }
38002             if (x + 1<width() && state(x + 1,y,z)!=1) {
38003               const Tfloat dist = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
38004               if (dist<res(x + 1,y,z)) {
38005                 res(x + 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
38006               }
38007             }
38008             if (y - 1>=0 && state(x,y - 1,z)!=1) {
38009               const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
38010               if (dist<res(x,y - 1,z)) {
38011                 res(x,y - 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
38012               }
38013             }
38014             if (y + 1<height() && state(x,y + 1,z)!=1) {
38015               const Tfloat dist = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
38016               if (dist<res(x,y + 1,z)) {
38017                 res(x,y + 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
38018               }
38019             }
38020             if (z - 1>=0 && state(x,y,z - 1)!=1) {
38021               const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
38022               if (dist<res(x,y,z - 1)) {
38023                 res(x,y,z - 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
38024               }
38025             }
38026             if (z + 1<depth() && state(x,y,z + 1)!=1) {
38027               const Tfloat dist = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
38028               if (dist<res(x,y,z + 1)) {
38029                 res(x,y,z + 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
38030               }
38031             }
38032           }
38033         }
38034       }
38035       return result;
38036     }
38037 
38038     // Locally solve eikonal equation.
38039     Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P,
38040                               const int x=0, const int y=0, const int z=0) const {
38041       const Tfloat M = (Tfloat)cimg::type<T>::max();
38042       T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 1<width()?res(x + 1,y,z):M);
38043       Tfloat root = 0;
38044       if (_depth>1) { // 3d.
38045         T
38046           T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M),
38047           T3 = (T)std::min(z - 1>=0?res(x,y,z - 1):M,z + 1<depth()?res(x,y,z + 1):M);
38048         if (T1>T2) cimg::swap(T1,T2);
38049         if (T2>T3) cimg::swap(T2,T3);
38050         if (T1>T2) cimg::swap(T1,T2);
38051         if (P<=0) return (Tfloat)T1;
38052         if (T3<M && ___distance_eikonal(3,-2*(T1 + T2 + T3),T1*T1 + T2*T2 + T3*T3 - P*P,root))
38053           return std::max((Tfloat)T3,root);
38054         if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
38055           return std::max((Tfloat)T2,root);
38056         return P + T1;
38057       } else if (_height>1) { // 2d.
38058         T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M);
38059         if (T1>T2) cimg::swap(T1,T2);
38060         if (P<=0) return (Tfloat)T1;
38061         if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
38062           return std::max((Tfloat)T2,root);
38063         return P + T1;
38064       } else { // 1d.
38065         if (P<=0) return (Tfloat)T1;
38066         return P + T1;
38067       }
38068       return 0;
38069     }
38070 
38071     // Find max root of a 2nd-order polynomial.
38072     static bool ___distance_eikonal(const Tfloat a, const Tfloat b, const Tfloat c, Tfloat &root) {
38073       const Tfloat delta = b*b - 4*a*c;
38074       if (delta<0) return false;
38075       root = 0.5f*(-b + std::sqrt(delta))/a;
38076       return true;
38077     }
38078 
38079     // Insert new point in heap.
38080     template<typename t>
38081     void _eik_priority_queue_insert(CImg<charT>& state, unsigned int& siz, const t value,
38082                                     const unsigned int x, const unsigned int y, const unsigned int z) {
38083       if (state(x,y,z)>0) return;
38084       state(x,y,z) = 0;
38085       if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
38086       (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z;
38087       for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
38088         cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
38089         cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
38090       }
38091     }
38092 
38093     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE.
38094     /**
38095        \param nb_iterations Number of PDE iterations.
38096        \param band_size Size of the narrow band.
38097        \param time_step Time step of the PDE iterations.
38098     **/
38099     CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
38100       if (is_empty()) return *this;
38101       CImg<Tfloat> velocity(*this,false);
38102       for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
38103         Tfloat *ptrd = velocity._data, veloc_max = 0;
38104         if (_depth>1) { // 3d
38105           CImg_3x3x3(I,Tfloat);
38106           cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
38107             const Tfloat
38108               gx = (Incc - Ipcc)/2,
38109               gy = (Icnc - Icpc)/2,
38110               gz = (Iccn - Iccp)/2,
38111               sgn = -cimg::sign(Iccc),
38112               ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
38113               iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
38114               iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
38115               ng = 1e-5f + cimg::hypot(gx,gy,gz),
38116               ngx = gx/ng,
38117               ngy = gy/ng,
38118               ngz = gz/ng,
38119               veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
38120             *(ptrd++) = veloc;
38121             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
38122           } else *(ptrd++) = 0;
38123         } else { // 2d version
38124           CImg_3x3(I,Tfloat);
38125           cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
38126             const Tfloat
38127               gx = (Inc - Ipc)/2,
38128               gy = (Icn - Icp)/2,
38129               sgn = -cimg::sign(Icc),
38130               ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
38131               iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
38132               ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)),
38133               ngx = gx/ng,
38134               ngy = gy/ng,
38135               veloc = sgn*(ngx*ix + ngy*iy - 1);
38136             *(ptrd++) = veloc;
38137             if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
38138           } else *(ptrd++) = 0;
38139         }
38140         if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
38141       }
38142       return *this;
38143     }
38144 
38145     //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance.
38146     CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0,
38147                                       const float time_step=0.5f) const {
38148       return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
38149     }
38150 
38151     //! Compute Haar multiscale wavelet transform.
38152     /**
38153        \param axis Axis considered for the transform.
38154        \param invert Set inverse of direct transform.
38155        \param nb_scales Number of scales used for the transform.
38156     **/
38157     CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
38158       return get_haar(axis,invert,nb_scales).move_to(*this);
38159     }
38160 
38161     //! Compute Haar multiscale wavelet transform \newinstance.
38162     CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
38163       if (is_empty() || !nb_scales) return +*this;
38164       CImg<Tfloat> res;
38165       const Tfloat sqrt2 = std::sqrt(2.0f);
38166       if (nb_scales==1) {
38167         switch (cimg::lowercase(axis)) { // Single scale transform
38168         case 'x' : {
38169           const unsigned int w = _width/2;
38170           if (w) {
38171             if ((w%2) && w!=1)
38172               throw CImgInstanceException(_cimg_instance
38173                                           "haar(): Sub-image width %u is not even.",
38174                                           cimg_instance,
38175                                           w);
38176 
38177             res.assign(_width,_height,_depth,_spectrum);
38178             if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
38179               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
38180                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
38181                 res(x2++,y,z,c) = (val0 - val1)/sqrt2;
38182                 res(x2++,y,z,c) = (val0 + val1)/sqrt2;
38183               }
38184             } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
38185               for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
38186                 const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
38187                 res(x,y,z,c) = (val0 + val1)/sqrt2;
38188                 res(xw,y,z,c) = (val1 - val0)/sqrt2;
38189               }
38190             }
38191           } else return *this;
38192         } break;
38193         case 'y' : {
38194           const unsigned int h = _height/2;
38195           if (h) {
38196             if ((h%2) && h!=1)
38197               throw CImgInstanceException(_cimg_instance
38198                                           "haar(): Sub-image height %u is not even.",
38199                                           cimg_instance,
38200                                           h);
38201 
38202             res.assign(_width,_height,_depth,_spectrum);
38203             if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
38204               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
38205                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
38206                 res(x,y2++,z,c) = (val0 - val1)/sqrt2;
38207                 res(x,y2++,z,c) = (val0 + val1)/sqrt2;
38208               }
38209             } else cimg_forXZC(*this,x,z,c) {
38210               for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
38211                 const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
38212                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
38213                 res(x,yh,z,c) = (val1 - val0)/sqrt2;
38214               }
38215             }
38216           } else return *this;
38217         } break;
38218         case 'z' : {
38219           const unsigned int d = _depth/2;
38220           if (d) {
38221             if ((d%2) && d!=1)
38222               throw CImgInstanceException(_cimg_instance
38223                                           "haar(): Sub-image depth %u is not even.",
38224                                           cimg_instance,
38225                                           d);
38226 
38227             res.assign(_width,_height,_depth,_spectrum);
38228             if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
38229               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
38230                 const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
38231                 res(x,y,z2++,c) = (val0 - val1)/sqrt2;
38232                 res(x,y,z2++,c) = (val0 + val1)/sqrt2;
38233               }
38234             } else cimg_forXYC(*this,x,y,c) {
38235               for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
38236                 const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
38237                 res(x,y,z,c)  = (val0 + val1)/sqrt2;
38238                 res(x,y,zd,c) = (val1 - val0)/sqrt2;
38239               }
38240             }
38241           } else return *this;
38242         } break;
38243         default :
38244           throw CImgArgumentException(_cimg_instance
38245                                       "haar(): Invalid specified axis '%c' "
38246                                       "(should be { x | y | z }).",
38247                                       cimg_instance,
38248                                       axis);
38249         }
38250       } else { // Multi-scale version
38251         if (invert) {
38252           res.assign(*this,false);
38253           switch (cimg::lowercase(axis)) {
38254           case 'x' : {
38255             unsigned int w = _width;
38256             for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
38257             for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w - 1).get_haar('x',true,1));
38258           } break;
38259           case 'y' : {
38260             unsigned int h = _width;
38261             for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
38262             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));
38263           } break;
38264           case 'z' : {
38265             unsigned int d = _depth;
38266             for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
38267             for (d = d?d:1; d<=_depth; d*=2)
38268               res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',true,1));
38269           } break;
38270           default :
38271             throw CImgArgumentException(_cimg_instance
38272                                         "haar(): Invalid specified axis '%c' "
38273                                         "(should be { x | y | z }).",
38274                                         cimg_instance,
38275                                         axis);
38276           }
38277         } else { // Direct transform
38278           res = get_haar(axis,false,1);
38279           switch (cimg::lowercase(axis)) {
38280           case 'x' : {
38281             for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
38282               res.draw_image(res.get_crop(0,w - 1).get_haar('x',false,1));
38283           } break;
38284           case 'y' : {
38285             for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
38286               res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',false,1));
38287           } break;
38288           case 'z' : {
38289             for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
38290               res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',false,1));
38291           } break;
38292           default :
38293             throw CImgArgumentException(_cimg_instance
38294                                         "haar(): Invalid specified axis '%c' "
38295                                         "(should be { x | y | z }).",
38296                                         cimg_instance,
38297                                         axis);
38298           }
38299         }
38300       }
38301       return res;
38302     }
38303 
38304     //! Compute Haar multiscale wavelet transform \overloading.
38305     /**
38306        \param invert Set inverse of direct transform.
38307        \param nb_scales Number of scales used for the transform.
38308     **/
38309     CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
38310       return get_haar(invert,nb_scales).move_to(*this);
38311     }
38312 
38313     //! Compute Haar multiscale wavelet transform \newinstance.
38314     CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
38315       CImg<Tfloat> res;
38316       if (nb_scales==1) { // Single scale transform
38317         if (_width>1) get_haar('x',invert,1).move_to(res);
38318         if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
38319         if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
38320         if (res) return res;
38321       } else { // Multi-scale transform
38322         if (invert) { // Inverse transform
38323           res.assign(*this,false);
38324           if (_width>1) {
38325             if (_height>1) {
38326               if (_depth>1) {
38327                 unsigned int w = _width, h = _height, d = _depth;
38328                 for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
38329                 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)
38330                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).get_haar(true,1));
38331               } else {
38332                 unsigned int w = _width, h = _height;
38333                 for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
38334                 for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
38335                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).get_haar(true,1));
38336               }
38337             } else {
38338               if (_depth>1) {
38339                 unsigned int w = _width, d = _depth;
38340                 for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
38341                 for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
38342                   res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).get_haar(true,1));
38343               } else {
38344                 unsigned int w = _width;
38345                 for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
38346                 for (w = w?w:1; w<=_width; w*=2)
38347                   res.draw_image(res.get_crop(0,0,0,w - 1,0,0).get_haar(true,1));
38348               }
38349             }
38350           } else {
38351             if (_height>1) {
38352               if (_depth>1) {
38353                 unsigned int h = _height, d = _depth;
38354                 for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
38355                 for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
38356                   res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).get_haar(true,1));
38357               } else {
38358                 unsigned int h = _height;
38359                 for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
38360                 for (h = h?h:1; h<=_height; h*=2)
38361                   res.draw_image(res.get_crop(0,0,0,0,h - 1,0).get_haar(true,1));
38362               }
38363             } else {
38364               if (_depth>1) {
38365                 unsigned int d = _depth;
38366                 for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
38367                 for (d = d?d:1; d<=_depth; d*=2)
38368                   res.draw_image(res.get_crop(0,0,0,0,0,d - 1).get_haar(true,1));
38369               } else return *this;
38370             }
38371           }
38372         } else { // Direct transform
38373           res = get_haar(false,1);
38374           if (_width>1) {
38375             if (_height>1) {
38376               if (_depth>1)
38377                 for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales;
38378                      ++s, w/=2, h/=2, d/=2)
38379                   res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).haar(false,1));
38380               else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
38381                      res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).haar(false,1));
38382             } else {
38383               if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
38384                               res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).haar(false,1));
38385               else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
38386                      res.draw_image(res.get_crop(0,0,0,w - 1,0,0).haar(false,1));
38387             }
38388           } else {
38389             if (_height>1) {
38390               if (_depth>1)
38391                 for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
38392                   res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).haar(false,1));
38393               else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
38394                      res.draw_image(res.get_crop(0,0,0,0,h - 1,0).haar(false,1));
38395             } else {
38396               if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
38397                               res.draw_image(res.get_crop(0,0,0,0,0,d - 1).haar(false,1));
38398               else return *this;
38399             }
38400           }
38401         }
38402         return res;
38403       }
38404       return *this;
38405     }
38406 
38407     //! Compute 1d Fast Fourier Transform, along a specified axis.
38408     /**
38409        \param axis Axis along which the FFT is computed.
38410        \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
38411     **/
38412     CImgList<Tfloat> get_FFT(const char axis, const bool is_invert=false) const {
38413       CImgList<Tfloat> res(*this,CImg<Tfloat>());
38414       CImg<Tfloat>::FFT(res[0],res[1],axis,is_invert);
38415       return res;
38416     }
38417 
38418     //! Compute n-d Fast Fourier Transform.
38419     /*
38420       \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
38421     **/
38422     CImgList<Tfloat> get_FFT(const bool is_invert=false) const {
38423       CImgList<Tfloat> res(*this,CImg<Tfloat>());
38424       CImg<Tfloat>::FFT(res[0],res[1],is_invert);
38425       return res;
38426     }
38427 
38428     //! Compute 1d Fast Fourier Transform, along a specified axis.
38429     /**
38430        \param[in,out] real Real part of the pixel values.
38431        \param[in,out] imag Imaginary part of the pixel values.
38432        \param axis Axis along which the FFT is computed.
38433        \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
38434     **/
38435     static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_invert=false) {
38436       if (!real)
38437         throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.",
38438                                     pixel_type());
38439 
38440       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
38441       if (!real.is_sameXYZC(imag))
38442         throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
38443                                     "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
38444                                     pixel_type(),
38445                                     real._width,real._height,real._depth,real._spectrum,real._data,
38446                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
38447 #ifdef cimg_use_fftw3
38448       cimg::mutex(12);
38449       fftw_complex *data_in;
38450       fftw_plan data_plan;
38451 
38452       switch (cimg::lowercase(axis)) {
38453       case 'x' : { // Fourier along X, using FFTW library.
38454         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width);
38455         if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
38456                                                   "for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
38457                                                   pixel_type(),
38458                                                   cimg::strbuffersize(sizeof(fftw_complex)*real._width),
38459                                                   real._width,real._height,real._depth,real._spectrum);
38460 
38461         data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
38462         cimg_forYZC(real,y,z,c) {
38463           T *ptrr = real.data(0,y,z,c), *ptri = imag.data(0,y,z,c);
38464           double *ptrd = (double*)data_in;
38465           cimg_forX(real,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); }
38466           fftw_execute(data_plan);
38467           const unsigned int fact = real._width;
38468           if (is_invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); }
38469           else cimg_forX(real,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); }
38470         }
38471       } break;
38472       case 'y' : { // Fourier along Y, using FFTW library.
38473         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._height);
38474         if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
38475                                                   "for computing FFT of image (%u,%u,%u,%u) along the Y-axis.",
38476                                                   pixel_type(),
38477                                                   cimg::strbuffersize(sizeof(fftw_complex)*real._height),
38478                                                   real._width,real._height,real._depth,real._spectrum);
38479 
38480         data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
38481         const unsigned int off = real._width;
38482         cimg_forXZC(real,x,z,c) {
38483           T *ptrr = real.data(x,0,z,c), *ptri = imag.data(x,0,z,c);
38484           double *ptrd = (double*)data_in;
38485           cimg_forY(real,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
38486           fftw_execute(data_plan);
38487           const unsigned int fact = real._height;
38488           if (is_invert)
38489             cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
38490           else cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
38491         }
38492       } break;
38493       case 'z' : { // Fourier along Z, using FFTW library.
38494         data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._depth);
38495         if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
38496                                                   "for computing FFT of image (%u,%u,%u,%u) along the Z-axis.",
38497                                                   pixel_type(),
38498                                                   cimg::strbuffersize(sizeof(fftw_complex)*real._depth),
38499                                                   real._width,real._height,real._depth,real._spectrum);
38500 
38501         data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
38502         const ulongT off = (ulongT)real._width*real._height;
38503         cimg_forXYC(real,x,y,c) {
38504           T *ptrr = real.data(x,y,0,c), *ptri = imag.data(x,y,0,c);
38505           double *ptrd = (double*)data_in;
38506           cimg_forZ(real,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
38507           fftw_execute(data_plan);
38508           const unsigned int fact = real._depth;
38509           if (is_invert)
38510             cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
38511           else cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
38512         }
38513       } break;
38514       default :
38515         throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
38516                                     "(%u,%u,%u,%u) "
38517                                     "(should be { x | y | z }).",
38518                                     pixel_type(),axis,
38519                                     real._width,real._height,real._depth,real._spectrum);
38520       }
38521       fftw_destroy_plan(data_plan);
38522       fftw_free(data_in);
38523       cimg::mutex(12,0);
38524 #else
38525       switch (cimg::lowercase(axis)) {
38526       case 'x' : { // Fourier along X, using built-in functions.
38527         const unsigned int N = real._width, N2 = N>>1;
38528         if (((N - 1)&N) && N!=1)
38529           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
38530                                       "have non 2^N dimension along the X-axis.",
38531                                       pixel_type(),
38532                                       real._width,real._height,real._depth,real._spectrum);
38533 
38534         for (unsigned int i = 0, j = 0; i<N2; ++i) {
38535           if (j>i) cimg_forYZC(real,y,z,c) {
38536               cimg::swap(real(i,y,z,c),real(j,y,z,c));
38537               cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
38538               if (j<N2) {
38539                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
38540                 cimg::swap(real(ri,y,z,c),real(rj,y,z,c));
38541                 cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
38542               }
38543             }
38544           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
38545         }
38546         for (unsigned int delta = 2; delta<=N; delta<<=1) {
38547           const unsigned int delta2 = delta>>1;
38548           for (unsigned int i = 0; i<N; i+=delta) {
38549             float wr = 1, wi = 0;
38550             const float
38551               angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
38552               ca = (float)std::cos(angle),
38553               sa = (float)std::sin(angle);
38554             for (unsigned int k = 0; k<delta2; ++k) {
38555               const unsigned int j = i + k, nj = j + delta2;
38556               cimg_forYZC(real,y,z,c) {
38557                 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);
38558                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
38559                 nir = (T)(ir - tmpr);
38560                 nii = (T)(ii - tmpi);
38561                 ir+=(T)tmpr;
38562                 ii+=(T)tmpi;
38563               }
38564               const float nwr = wr*ca-wi*sa;
38565               wi = wi*ca + wr*sa;
38566               wr = nwr;
38567             }
38568           }
38569         }
38570         if (is_invert) { real/=N; imag/=N; }
38571       } break;
38572       case 'y' : { // Fourier along Y, using built-in functions.
38573         const unsigned int N = real._height, N2 = N>>1;
38574         if (((N - 1)&N) && N!=1)
38575           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
38576                                       "have non 2^N dimension along the Y-axis.",
38577                                       pixel_type(),
38578                                       real._width,real._height,real._depth,real._spectrum);
38579 
38580         for (unsigned int i = 0, j = 0; i<N2; ++i) {
38581           if (j>i) cimg_forXZC(real,x,z,c) {
38582               cimg::swap(real(x,i,z,c),real(x,j,z,c));
38583               cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
38584               if (j<N2) {
38585                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
38586                 cimg::swap(real(x,ri,z,c),real(x,rj,z,c));
38587                 cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
38588               }
38589             }
38590           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
38591         }
38592         for (unsigned int delta = 2; delta<=N; delta<<=1) {
38593           const unsigned int delta2 = (delta>>1);
38594           for (unsigned int i = 0; i<N; i+=delta) {
38595             float wr = 1, wi = 0;
38596             const float
38597               angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
38598               ca = (float)std::cos(angle),
38599               sa = (float)std::sin(angle);
38600             for (unsigned int k = 0; k<delta2; ++k) {
38601               const unsigned int j = i + k, nj = j + delta2;
38602               cimg_forXZC(real,x,z,c) {
38603                 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);
38604                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
38605                 nir = (T)(ir - tmpr);
38606                 nii = (T)(ii - tmpi);
38607                 ir+=(T)tmpr;
38608                 ii+=(T)tmpi;
38609               }
38610               const float nwr = wr*ca-wi*sa;
38611               wi = wi*ca + wr*sa;
38612               wr = nwr;
38613             }
38614           }
38615         }
38616         if (is_invert) { real/=N; imag/=N; }
38617       } break;
38618       case 'z' : { // Fourier along Z, using built-in functions.
38619         const unsigned int N = real._depth, N2 = N>>1;
38620         if (((N - 1)&N) && N!=1)
38621           throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
38622                                       "have non 2^N dimension along the Z-axis.",
38623                                       pixel_type(),
38624                                       real._width,real._height,real._depth,real._spectrum);
38625 
38626         for (unsigned int i = 0, j = 0; i<N2; ++i) {
38627           if (j>i) cimg_forXYC(real,x,y,c) {
38628               cimg::swap(real(x,y,i,c),real(x,y,j,c));
38629               cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
38630               if (j<N2) {
38631                 const unsigned int ri = N - 1 - i, rj = N - 1 - j;
38632                 cimg::swap(real(x,y,ri,c),real(x,y,rj,c));
38633                 cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
38634               }
38635             }
38636           for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
38637         }
38638         for (unsigned int delta = 2; delta<=N; delta<<=1) {
38639           const unsigned int delta2 = (delta>>1);
38640           for (unsigned int i = 0; i<N; i+=delta) {
38641             float wr = 1, wi = 0;
38642             const float
38643               angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
38644               ca = (float)std::cos(angle),
38645               sa = (float)std::sin(angle);
38646             for (unsigned int k = 0; k<delta2; ++k) {
38647               const unsigned int j = i + k, nj = j + delta2;
38648               cimg_forXYC(real,x,y,c) {
38649                 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);
38650                 const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
38651                 nir = (T)(ir - tmpr);
38652                 nii = (T)(ii - tmpi);
38653                 ir+=(T)tmpr;
38654                 ii+=(T)tmpi;
38655               }
38656               const float nwr = wr*ca-wi*sa;
38657               wi = wi*ca + wr*sa;
38658               wr = nwr;
38659             }
38660           }
38661         }
38662         if (is_invert) { real/=N; imag/=N; }
38663       } break;
38664       default :
38665         throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
38666                                     "(%u,%u,%u,%u) "
38667                                     "(should be { x | y | z }).",
38668                                     pixel_type(),axis,
38669                                     real._width,real._height,real._depth,real._spectrum);
38670       }
38671 #endif
38672     }
38673 
38674     //! Compute n-d Fast Fourier Transform.
38675     /**
38676        \param[in,out] real Real part of the pixel values.
38677        \param[in,out] imag Imaginary part of the pixel values.
38678        \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
38679        \param nb_threads Number of parallel threads used for the computation.
38680          Use \c 0 to set this to the number of available cpus.
38681     **/
38682     static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_invert=false, const unsigned int nb_threads=0) {
38683       if (!real)
38684         throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.",
38685                                     pixel_type());
38686 
38687       if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
38688       if (!real.is_sameXYZC(imag))
38689         throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
38690                                     "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
38691                                     pixel_type(),
38692                                     real._width,real._height,real._depth,real._spectrum,real._data,
38693                                     imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
38694 
38695 #ifdef cimg_use_fftw3
38696       cimg::mutex(12);
38697 #ifndef cimg_use_fftw3_singlethread
38698       const unsigned int _nb_threads = nb_threads?nb_threads:cimg::nb_cpus();
38699       static int fftw_st = fftw_init_threads();
38700       cimg::unused(fftw_st);
38701       fftw_plan_with_nthreads(_nb_threads);
38702 #else
38703       cimg::unused(nb_threads);
38704 #endif
38705       fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
38706       if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
38707                                                 "for computing FFT of image (%u,%u,%u,%u).",
38708                                                 pixel_type(),
38709                                                 cimg::strbuffersize(sizeof(fftw_complex)*real._width*
38710                                                                     real._height*real._depth*real._spectrum),
38711                                                 real._width,real._height,real._depth,real._spectrum);
38712 
38713       fftw_plan data_plan;
38714       const ulongT w = (ulongT)real._width, wh = w*real._height, whd = wh*real._depth;
38715       data_plan = fftw_plan_dft_3d(real._width,real._height,real._depth,data_in,data_in,
38716                                    is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
38717       cimg_forC(real,c) {
38718         T *ptrr = real.data(0,0,0,c), *ptri = imag.data(0,0,0,c);
38719         double *ptrd = (double*)data_in;
38720         for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh - 1, ptri-=wh - 1)
38721           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
38722             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
38723               *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri;
38724             }
38725         fftw_execute(data_plan);
38726         ptrd = (double*)data_in;
38727         ptrr = real.data(0,0,0,c);
38728         ptri = imag.data(0,0,0,c);
38729         if (!is_invert) for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh - 1, ptri-=wh - 1)
38730           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
38731             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
38732               *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++);
38733             }
38734         else for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh - 1, ptri-=wh - 1)
38735           for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
38736             for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
38737               *ptrr = (T)(*(ptrd++)/whd); *ptri = (T)(*(ptrd++)/whd);
38738             }
38739       }
38740       fftw_destroy_plan(data_plan);
38741       fftw_free(data_in);
38742 #ifndef cimg_use_fftw3_singlethread
38743       fftw_cleanup_threads();
38744 #endif
38745       cimg::mutex(12,0);
38746 #else
38747       cimg::unused(nb_threads);
38748       if (real._depth>1) FFT(real,imag,'z',is_invert);
38749       if (real._height>1) FFT(real,imag,'y',is_invert);
38750       if (real._width>1) FFT(real,imag,'x',is_invert);
38751 #endif
38752     }
38753 
38754     //@}
38755     //-------------------------------------
38756     //
38757     //! \name 3d Objects Management
38758     //@{
38759     //-------------------------------------
38760 
38761     //! Shift 3d object's vertices.
38762     /**
38763        \param tx X-coordinate of the 3d displacement vector.
38764        \param ty Y-coordinate of the 3d displacement vector.
38765        \param tz Z-coordinate of the 3d displacement vector.
38766     **/
38767     CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
38768       if (_height!=3 || _depth>1 || _spectrum>1)
38769         throw CImgInstanceException(_cimg_instance
38770                                     "shift_object3d(): Instance is not a set of 3d vertices.",
38771                                     cimg_instance);
38772 
38773       get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
38774       return *this;
38775     }
38776 
38777     //! Shift 3d object's vertices \newinstance.
38778     CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
38779       return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
38780     }
38781 
38782     //! Shift 3d object's vertices, so that it becomes centered.
38783     /**
38784        \note The object center is computed as its barycenter.
38785     **/
38786     CImg<T>& shift_object3d() {
38787       if (_height!=3 || _depth>1 || _spectrum>1)
38788         throw CImgInstanceException(_cimg_instance
38789                                     "shift_object3d(): Instance is not a set of 3d vertices.",
38790                                     cimg_instance);
38791 
38792       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
38793       float
38794         xm, xM = (float)xcoords.max_min(xm),
38795         ym, yM = (float)ycoords.max_min(ym),
38796         zm, zM = (float)zcoords.max_min(zm);
38797       xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
38798       return *this;
38799     }
38800 
38801     //! Shift 3d object's vertices, so that it becomes centered \newinstance.
38802     CImg<Tfloat> get_shift_object3d() const {
38803       return CImg<Tfloat>(*this,false).shift_object3d();
38804     }
38805 
38806     //! Resize 3d object.
38807     /**
38808        \param sx Width of the 3d object's bounding box.
38809        \param sy Height of the 3d object's bounding box.
38810        \param sz Depth of the 3d object's bounding box.
38811     **/
38812     CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
38813       if (_height!=3 || _depth>1 || _spectrum>1)
38814         throw CImgInstanceException(_cimg_instance
38815                                     "resize_object3d(): Instance is not a set of 3d vertices.",
38816                                     cimg_instance);
38817 
38818       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
38819       float
38820         xm, xM = (float)xcoords.max_min(xm),
38821         ym, yM = (float)ycoords.max_min(ym),
38822         zm, zM = (float)zcoords.max_min(zm);
38823       if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
38824       if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
38825       if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
38826       return *this;
38827     }
38828 
38829     //! Resize 3d object \newinstance.
38830     CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
38831       return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
38832     }
38833 
38834     //! Resize 3d object to unit size.
38835     CImg<T> resize_object3d() {
38836       if (_height!=3 || _depth>1 || _spectrum>1)
38837         throw CImgInstanceException(_cimg_instance
38838                                     "resize_object3d(): Instance is not a set of 3d vertices.",
38839                                     cimg_instance);
38840 
38841       CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
38842       float
38843         xm, xM = (float)xcoords.max_min(xm),
38844         ym, yM = (float)ycoords.max_min(ym),
38845         zm, zM = (float)zcoords.max_min(zm);
38846       const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
38847       if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
38848       return *this;
38849     }
38850 
38851     //! Resize 3d object to unit size \newinstance.
38852     CImg<Tfloat> get_resize_object3d() const {
38853       return CImg<Tfloat>(*this,false).resize_object3d();
38854     }
38855 
38856     //! Merge two 3d objects together.
38857     /**
38858        \param[in,out] primitives Primitives data of the current 3d object.
38859        \param obj_vertices Vertices data of the additional 3d object.
38860        \param obj_primitives Primitives data of the additional 3d object.
38861     **/
38862     template<typename tf, typename tp, typename tff>
38863     CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices,
38864                              const CImgList<tff>& obj_primitives) {
38865       if (!obj_vertices || !obj_primitives) return *this;
38866       if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
38867         throw CImgInstanceException(_cimg_instance
38868                                     "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a "
38869                                     "set of 3d vertices.",
38870                                     cimg_instance,
38871                                     obj_vertices._width,obj_vertices._height,
38872                                     obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
38873 
38874       if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
38875       if (_height!=3 || _depth>1 || _spectrum>1)
38876         throw CImgInstanceException(_cimg_instance
38877                                     "append_object3d(): Instance is not a set of 3d vertices.",
38878                                     cimg_instance);
38879 
38880       const unsigned int P = _width;
38881       append(obj_vertices,'x');
38882       const unsigned int N = primitives._width;
38883       primitives.insert(obj_primitives);
38884       for (unsigned int i = N; i<primitives._width; ++i) {
38885         CImg<tf> &p = primitives[i];
38886         switch (p.size()) {
38887         case 1 : p[0]+=P; break; // Point.
38888         case 5 : p[0]+=P; p[1]+=P; break; // Sphere.
38889         case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment.
38890         case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle.
38891         case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle.
38892         }
38893       }
38894       return *this;
38895     }
38896 
38897     //! Texturize primitives of a 3d object.
38898     /**
38899        \param[in,out] primitives Primitives data of the 3d object.
38900        \param[in,out] colors Colors data of the 3d object.
38901        \param texture Texture image to map to 3d object.
38902        \param coords Texture-mapping coordinates.
38903     **/
38904     template<typename tp, typename tc, typename tt, typename tx>
38905     const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors,
38906                                       const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::const_empty()) const {
38907       if (is_empty()) return *this;
38908       if (_height!=3)
38909         throw CImgInstanceException(_cimg_instance
38910                                     "texturize_object3d(): image instance is not a set of 3d points.",
38911                                     cimg_instance);
38912       if (coords && (coords._width!=_width || coords._height!=2))
38913         throw CImgArgumentException(_cimg_instance
38914                                     "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
38915                                     cimg_instance,
38916                                     coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
38917       CImg<intT> _coords;
38918       if (!coords) { // If no texture coordinates specified, do a default XY-projection.
38919         _coords.assign(_width,2);
38920         float
38921           xmin, xmax = (float)get_shared_row(0).max_min(xmin),
38922           ymin, ymax = (float)get_shared_row(1).max_min(ymin),
38923           dx = xmax>xmin?xmax-xmin:1,
38924           dy = ymax>ymin?ymax-ymin:1;
38925         cimg_forX(*this,p) {
38926           _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx);
38927           _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy);
38928         }
38929       } else _coords = coords;
38930 
38931       int texture_ind = -1;
38932       cimglist_for(primitives,l) {
38933         CImg<tp> &p = primitives[l];
38934         const unsigned int siz = p.size();
38935         switch (siz) {
38936         case 1 : { // Point.
38937           const unsigned int i0 = (unsigned int)p[0];
38938           const int x0 = _coords(i0,0), y0 = _coords(i0,1);
38939           texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
38940                                 y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]);
38941         } break;
38942         case 2 : case 6 : { // Line.
38943           const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1];
38944           const int
38945             x0 = _coords(i0,0), y0 = _coords(i0,1),
38946             x1 = _coords(i1,0), y1 = _coords(i1,1);
38947           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
38948           else colors[l].assign(colors[texture_ind],true);
38949           CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
38950         } break;
38951         case 3 : case 9 : { // Triangle.
38952           const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2];
38953           const int
38954             x0 = _coords(i0,0), y0 = _coords(i0,1),
38955             x1 = _coords(i1,0), y1 = _coords(i1,1),
38956             x2 = _coords(i2,0), y2 = _coords(i2,1);
38957           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
38958           else colors[l].assign(colors[texture_ind],true);
38959           CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
38960         } break;
38961         case 4 : case 12 : { // Quadrangle.
38962           const unsigned int
38963             i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3];
38964           const int
38965             x0 = _coords(i0,0), y0 = _coords(i0,1),
38966             x1 = _coords(i1,0), y1 = _coords(i1,1),
38967             x2 = _coords(i2,0), y2 = _coords(i2,1),
38968             x3 = _coords(i3,0), y3 = _coords(i3,1);
38969           if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
38970           else colors[l].assign(colors[texture_ind],true);
38971           CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
38972         } break;
38973         }
38974       }
38975       return *this;
38976     }
38977 
38978     //! Generate a 3d elevation of the image instance.
38979     /**
38980        \param[out] primitives The returned list of the 3d object primitives
38981                               (template type \e tf should be at least \e unsigned \e int).
38982        \param[out] colors The returned list of the 3d object colors.
38983        \param elevation The input elevation map.
38984        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
38985        \par Example
38986        \code
38987        const CImg<float> img("reference.jpg");
38988        CImgList<unsigned int> faces3d;
38989        CImgList<unsigned char> colors3d;
38990        const CImg<float> points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2);
38991        CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d);
38992        \endcode
38993        \image html ref_elevation3d.jpg
38994     **/
38995     template<typename tf, typename tc, typename te>
38996     CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
38997       if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
38998         throw CImgArgumentException(_cimg_instance
38999                                     "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) "
39000                                     "have incompatible dimensions.",
39001                                     cimg_instance,
39002                                     elevation._width,elevation._height,elevation._depth,
39003                                     elevation._spectrum,elevation._data);
39004       if (is_empty()) return *this;
39005       float m, M = (float)max_min(m);
39006       if (M==m) ++M;
39007       colors.assign();
39008       const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
39009       for (unsigned int y = 0; y<size_y1; ++y)
39010         for (unsigned int x = 0; x<size_x1; ++x) {
39011           const unsigned char
39012             r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
39013             g = (unsigned char)(_spectrum>1?((*this)(x,y,1) - m)*255/(M-m):r),
39014             b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r);
39015           CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
39016         }
39017       const typename CImg<te>::_functor2d_int func(elevation);
39018       return elevation3d(primitives,func,0,0,_width - 1.0f,_height - 1.0f,_width,_height);
39019     }
39020 
39021     //! Generate the 3d projection planes of the image instance.
39022     /**
39023        \param[out] primitives Primitives data of the returned 3d object.
39024        \param[out] colors Colors data of the returned 3d object.
39025        \param x0 X-coordinate of the projection point.
39026        \param y0 Y-coordinate of the projection point.
39027        \param z0 Z-coordinate of the projection point.
39028        \param normalize_colors Tells if the created textures have normalized colors.
39029     **/
39030     template<typename tf, typename tc>
39031     CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors,
39032                                    const unsigned int x0, const unsigned int y0, const unsigned int z0,
39033                                    const bool normalize_colors=false) const {
39034       float m = 0, M = 0, delta = 1;
39035       if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
39036       const unsigned int
39037         _x0 = (x0>=_width)?_width - 1:x0,
39038         _y0 = (y0>=_height)?_height - 1:y0,
39039         _z0 = (z0>=_depth)?_depth - 1:z0;
39040       CImg<tc> img_xy, img_xz, img_yz;
39041       if (normalize_colors) {
39042         ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy);
39043         ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1).
39044           move_to(img_xz);
39045         ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1).
39046           move_to(img_yz);
39047       } else {
39048         get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy);
39049         get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
39050         get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
39051       }
39052       CImg<floatT> points(12,3,1,1,
39053                           0,_width - 1,_width - 1,0,   0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0,
39054                           0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0,       0,_height - 1,_height - 1,0,
39055                           _z0,_z0,_z0,_z0,         0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1);
39056       primitives.assign();
39057       CImg<tf>::vector(0,1,2,3,0,0,img_xy._width - 1,0,img_xy._width - 1,img_xy._height - 1,0,img_xy._height - 1).
39058         move_to(primitives);
39059       CImg<tf>::vector(4,5,6,7,0,0,img_xz._width - 1,0,img_xz._width - 1,img_xz._height - 1,0,img_xz._height - 1).
39060         move_to(primitives);
39061       CImg<tf>::vector(8,9,10,11,0,0,img_yz._width - 1,0,img_yz._width - 1,img_yz._height - 1,0,img_yz._height - 1).
39062         move_to(primitives);
39063       colors.assign();
39064       img_xy.move_to(colors);
39065       img_xz.move_to(colors);
39066       img_yz.move_to(colors);
39067       return points;
39068     }
39069 
39070     //! Generate a isoline of the image instance as a 3d object.
39071     /**
39072        \param[out] primitives The returned list of the 3d object primitives
39073                               (template type \e tf should be at least \e unsigned \e int).
39074        \param isovalue The returned list of the 3d object colors.
39075        \param size_x The number of subdivisions along the X-axis.
39076        \param size_y The number of subdisivions along the Y-axis.
39077        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
39078        \par Example
39079        \code
39080        const CImg<float> img("reference.jpg");
39081        CImgList<unsigned int> faces3d;
39082        const CImg<float> points3d = img.get_isoline3d(faces3d,100);
39083        CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d);
39084        \endcode
39085        \image html ref_isoline3d.jpg
39086     **/
39087     template<typename tf>
39088     CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
39089                                const int size_x=-100, const int size_y=-100) const {
39090       if (_spectrum>1)
39091         throw CImgInstanceException(_cimg_instance
39092                                     "get_isoline3d(): Instance is not a scalar image.",
39093                                     cimg_instance);
39094       if (_depth>1)
39095         throw CImgInstanceException(_cimg_instance
39096                                     "get_isoline3d(): Instance is not a 2d image.",
39097                                     cimg_instance);
39098       primitives.assign();
39099       if (is_empty()) return *this;
39100       CImg<floatT> vertices;
39101       if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
39102         const _functor2d_int func(*this);
39103         vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.0f,height() - 1.0f,width(),height());
39104       } else {
39105         const _functor2d_float func(*this);
39106         vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.0f,height() - 1.0f,size_x,size_y);
39107       }
39108       return vertices;
39109     }
39110 
39111     //! Generate an isosurface of the image instance as a 3d object.
39112     /**
39113        \param[out] primitives The returned list of the 3d object primitives
39114                               (template type \e tf should be at least \e unsigned \e int).
39115        \param isovalue The returned list of the 3d object colors.
39116        \param size_x Number of subdivisions along the X-axis.
39117        \param size_y Number of subdisivions along the Y-axis.
39118        \param size_z Number of subdisivions along the Z-axis.
39119        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
39120        \par Example
39121        \code
39122        const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
39123        CImgList<unsigned int> faces3d;
39124        const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
39125        CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d);
39126        \endcode
39127        \image html ref_isosurface3d.jpg
39128     **/
39129     template<typename tf>
39130     CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
39131                                   const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
39132       if (_spectrum>1)
39133         throw CImgInstanceException(_cimg_instance
39134                                     "get_isosurface3d(): Instance is not a scalar image.",
39135                                     cimg_instance);
39136       primitives.assign();
39137       if (is_empty()) return *this;
39138       CImg<floatT> vertices;
39139       if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
39140         const _functor3d_int func(*this);
39141         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.0f,height() - 1.0f,depth() - 1.0f,
39142                                 width(),height(),depth());
39143       } else {
39144         const _functor3d_float func(*this);
39145         vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.0f,height() - 1.0f,depth() - 1.0f,
39146                                 size_x,size_y,size_z);
39147       }
39148       return vertices;
39149     }
39150 
39151     //! Compute 3d elevation of a function as a 3d object.
39152     /**
39153        \param[out] primitives Primitives data of the resulting 3d object.
39154        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
39155        \param x0 X-coordinate of the starting point.
39156        \param y0 Y-coordinate of the starting point.
39157        \param x1 X-coordinate of the ending point.
39158        \param y1 Y-coordinate of the ending point.
39159        \param size_x Resolution of the function along the X-axis.
39160        \param size_y Resolution of the function along the Y-axis.
39161     **/
39162     template<typename tf, typename tfunc>
39163     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
39164                                     const float x0, const float y0, const float x1, const float y1,
39165                                     const int size_x=256, const int size_y=256) {
39166       const float
39167         nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
39168         nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
39169       const unsigned int
39170         _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100),
39171         nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
39172         _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100),
39173         nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
39174       if (nsize_x<2 || nsize_y<2)
39175         throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).",
39176                                     pixel_type(),
39177                                     nsize_x,nsize_y);
39178 
39179       CImg<floatT> vertices(nsize_x*nsize_y,3);
39180       floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
39181       for (unsigned int y = 0; y<nsize_y; ++y) {
39182         const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
39183         for (unsigned int x = 0; x<nsize_x; ++x) {
39184           const float X = nx0 + x*(nx1-nx0)/nsize_x1;
39185           *(ptr_x++) = (float)x;
39186           *(ptr_y++) = (float)y;
39187           *(ptr_z++) = (float)func(X,Y);
39188         }
39189       }
39190       primitives.assign(nsize_x1*nsize_y1,1,4);
39191       for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
39192         const unsigned int yw = y*nsize_x;
39193         for (unsigned int x = 0; x<nsize_x1; ++x) {
39194           const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
39195           primitives[p++].fill(xpyw,xpyww,xpyww + 1,xpyw + 1);
39196         }
39197       }
39198       return vertices;
39199     }
39200 
39201     //! Compute 3d elevation of a function, as a 3d object \overloading.
39202     template<typename tf>
39203     static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
39204                                     const float x0, const float y0, const float x1, const float y1,
39205                                     const int size_x=256, const int size_y=256) {
39206       const _functor2d_expr func(expression);
39207       return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
39208     }
39209 
39210     //! Compute 0-isolines of a function, as a 3d object.
39211     /**
39212        \param[out] primitives Primitives data of the resulting 3d object.
39213        \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
39214        \param isovalue Isovalue to extract from function.
39215        \param x0 X-coordinate of the starting point.
39216        \param y0 Y-coordinate of the starting point.
39217        \param x1 X-coordinate of the ending point.
39218        \param y1 Y-coordinate of the ending point.
39219        \param size_x Resolution of the function along the X-axis.
39220        \param size_y Resolution of the function along the Y-axis.
39221        \note Use the marching squares algorithm for extracting the isolines.
39222      **/
39223     template<typename tf, typename tfunc>
39224     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
39225                                   const float x0, const float y0, const float x1, const float y1,
39226                                   const int size_x=256, const int size_y=256) {
39227       static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc,
39228                                               0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
39229       static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
39230                                            { 1,2,-1,-1 },   { 0,1,2,3 },   { 0,2,-1,-1 }, { 2,3,-1,-1 },
39231                                            { 2,3,-1,-1 },   { 0,2,-1,-1},  { 0,3,1,2 },   { 1,2,-1,-1 },
39232                                            { 1,3,-1,-1 },   { 0,1,-1,-1},  { 0,3,-1,-1},  { -1,-1,-1,-1 } };
39233       const unsigned int
39234         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
39235         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
39236         nx = _nx?_nx:1,
39237         ny = _ny?_ny:1,
39238         nxm1 = nx - 1,
39239         nym1 = ny - 1;
39240       primitives.assign();
39241       if (!nxm1 || !nym1) return CImg<floatT>();
39242       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
39243       CImgList<floatT> vertices;
39244       CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
39245       CImg<floatT> values1(nx), values2(nx);
39246       float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
39247 
39248       // Fill first line with values
39249       cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
39250 
39251       // Run the marching squares algorithm
39252       for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
39253         X = x0; nX = X + dx;
39254         indices2.fill(-1);
39255         for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
39256 
39257           // Determine square configuration
39258           const float
39259             val0 = values1(xi),
39260             val1 = values1(nxi),
39261             val2 = values2(nxi) = (float)func(nX,nY),
39262             val3 = values2(xi) = (float)func(X,nY);
39263           const unsigned int
39264             configuration = (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) |
39265             (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U),
39266             edge = edges[configuration];
39267 
39268           // Compute intersection vertices
39269           if (edge) {
39270             if ((edge&1) && indices1(xi,0)<0) {
39271               const float Xi = X + (isovalue-val0)*dx/(val1-val0);
39272               indices1(xi,0) = vertices.width();
39273               CImg<floatT>::vector(Xi,Y,0).move_to(vertices);
39274             }
39275             if ((edge&2) && indices1(nxi,1)<0) {
39276               const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
39277               indices1(nxi,1) = vertices.width();
39278               CImg<floatT>::vector(nX,Yi,0).move_to(vertices);
39279             }
39280             if ((edge&4) && indices2(xi,0)<0) {
39281               const float Xi = X + (isovalue-val3)*dx/(val2-val3);
39282               indices2(xi,0) = vertices.width();
39283               CImg<floatT>::vector(Xi,nY,0).move_to(vertices);
39284             }
39285             if ((edge&8) && indices1(xi,1)<0) {
39286               const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
39287               indices1(xi,1) = vertices.width();
39288               CImg<floatT>::vector(X,Yi,0).move_to(vertices);
39289             }
39290 
39291             // Create segments
39292             for (const int *segment = segments[configuration]; *segment!=-1; ) {
39293               const unsigned int p0 = (unsigned int)*(segment++), p1 = (unsigned int)*(segment++);
39294               const tf
39295                 i0 = (tf)(_isoline3d_indice(p0,indices1,indices2,xi,nxi)),
39296                 i1 = (tf)(_isoline3d_indice(p1,indices1,indices2,xi,nxi));
39297               CImg<tf>::vector(i0,i1).move_to(primitives);
39298             }
39299           }
39300         }
39301         values1.swap(values2);
39302         indices1.swap(indices2);
39303       }
39304       return vertices>'x';
39305     }
39306 
39307     //! Compute isolines of a function, as a 3d object \overloading.
39308     template<typename tf>
39309     static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
39310                                   const float x0, const float y0, const float x1, const float y1,
39311                                   const int size_x=256, const int size_y=256) {
39312       const _functor2d_expr func(expression);
39313       return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
39314     }
39315 
39316     template<typename t>
39317     static int _isoline3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
39318                                  const unsigned int x, const unsigned int nx) {
39319       switch (edge) {
39320       case 0 : return (int)indices1(x,0);
39321       case 1 : return (int)indices1(nx,1);
39322       case 2 : return (int)indices2(x,0);
39323       case 3 : return (int)indices1(x,1);
39324       }
39325       return 0;
39326     }
39327 
39328     //! Compute isosurface of a function, as a 3d object.
39329     /**
39330        \param[out] primitives Primitives data of the resulting 3d object.
39331        \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
39332        \param isovalue Isovalue to extract.
39333        \param x0 X-coordinate of the starting point.
39334        \param y0 Y-coordinate of the starting point.
39335        \param z0 Z-coordinate of the starting point.
39336        \param x1 X-coordinate of the ending point.
39337        \param y1 Y-coordinate of the ending point.
39338        \param z1 Z-coordinate of the ending point.
39339        \param size_x Resolution of the elevation function along the X-axis.
39340        \param size_y Resolution of the elevation function along the Y-axis.
39341        \param size_z Resolution of the elevation function along the Z-axis.
39342        \note Use the marching cubes algorithm for extracting the isosurface.
39343      **/
39344     template<typename tf, typename tfunc>
39345     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
39346                                      const float x0, const float y0, const float z0,
39347                                      const float x1, const float y1, const float z1,
39348                                      const int size_x=32, const int size_y=32, const int size_z=32) {
39349       static const unsigned int edges[256] = {
39350         0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
39351         0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
39352         0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
39353         0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
39354         0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
39355         0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
39356         0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
39357         0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
39358         0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
39359         0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
39360         0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
39361         0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
39362         0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
39363         0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
39364         0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
39365         0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
39366       };
39367 
39368       static const int triangles[256][16] = {
39369         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39370         { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39371         { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39372         { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39373         { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39374         { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39375         { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39376         { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
39377         { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39378         { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39379         { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39380         { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
39381         { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39382         { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
39383         { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
39384         { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39385         { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39386         { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39387         { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39388         { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
39389         { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39390         { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
39391         { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
39392         { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
39393         { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39394         { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
39395         { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
39396         { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
39397         { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 },
39398         { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
39399         { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
39400         { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
39401         { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39402         { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39403         { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39404         { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
39405         { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39406         { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
39407         { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
39408         { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
39409         { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39410         { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
39411         { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
39412         { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
39413         { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 },
39414         { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
39415         { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
39416         { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
39417         { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39418         { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
39419         { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
39420         { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39421         { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 },
39422         { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
39423         { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 },
39424         { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
39425         { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 },
39426         { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
39427         { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 },
39428         { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
39429         { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 },
39430         { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
39431         { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 },
39432         { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39433         { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39434         { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39435         { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39436         { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
39437         { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39438         { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
39439         { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
39440         { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
39441         { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39442         { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
39443         { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
39444         { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
39445         { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
39446         { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
39447         { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 },
39448         { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
39449         { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39450         { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
39451         { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
39452         { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
39453         { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 },
39454         { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
39455         { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 },
39456         { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
39457         { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
39458         { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
39459         { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 },
39460         { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
39461         { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
39462         { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
39463         { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 },
39464         { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
39465         { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39466         { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
39467         { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
39468         { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
39469         { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
39470         { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
39471         { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39472         { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
39473         { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 },
39474         { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
39475         { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
39476         { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
39477         { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 },
39478         { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
39479         { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
39480         { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39481         { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
39482         { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
39483         { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 },
39484         { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
39485         { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
39486         { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
39487         { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
39488         { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39489         { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
39490         { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
39491         { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 },
39492         { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
39493         { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 },
39494         { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39495         { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 },
39496         { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39497         { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39498         { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39499         { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39500         { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
39501         { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39502         { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
39503         { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
39504         { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
39505         { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39506         { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
39507         { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 },
39508         { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
39509         { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
39510         { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
39511         { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 },
39512         { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
39513         { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39514         { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
39515         { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 },
39516         { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
39517         { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 },
39518         { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
39519         { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 },
39520         { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
39521         { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
39522         { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39523         { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 },
39524         { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
39525         { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 },
39526         { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
39527         { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 },
39528         { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39529         { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39530         { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
39531         { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
39532         { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
39533         { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
39534         { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
39535         { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 },
39536         { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
39537         { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 },
39538         { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
39539         { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 },
39540         { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
39541         { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 },
39542         { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
39543         { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 },
39544         { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
39545         { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
39546         { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
39547         { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 },
39548         { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
39549         { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 },
39550         { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
39551         { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 },
39552         { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
39553         { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 },
39554         { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
39555         { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 },
39556         { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39557         { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 },
39558         { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
39559         { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39560         { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39561         { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39562         { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
39563         { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 },
39564         { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
39565         { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
39566         { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
39567         { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 },
39568         { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
39569         { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
39570         { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
39571         { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 },
39572         { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
39573         { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39574         { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
39575         { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
39576         { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39577         { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
39578         { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
39579         { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 },
39580         { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
39581         { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 },
39582         { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
39583         { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 },
39584         { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39585         { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 },
39586         { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
39587         { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 },
39588         { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
39589         { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
39590         { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39591         { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 },
39592         { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39593         { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
39594         { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
39595         { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 },
39596         { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
39597         { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 },
39598         { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
39599         { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
39600         { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
39601         { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 },
39602         { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
39603         { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 },
39604         { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39605         { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
39606         { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
39607         { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39608         { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39609         { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39610         { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
39611         { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
39612         { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39613         { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
39614         { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
39615         { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39616         { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39617         { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
39618         { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39619         { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 },
39620         { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39621         { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39622         { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39623         { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
39624         { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
39625       };
39626 
39627       const unsigned int
39628         _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
39629         _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
39630         _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
39631         nx = _nx?_nx:1,
39632         ny = _ny?_ny:1,
39633         nz = _nz?_nz:1,
39634         nxm1 = nx - 1,
39635         nym1 = ny - 1,
39636         nzm1 = nz - 1;
39637       primitives.assign();
39638       if (!nxm1 || !nym1 || !nzm1) return CImg<floatT>();
39639       const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
39640       CImgList<floatT> vertices;
39641       CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
39642       CImg<floatT> values1(nx,ny), values2(nx,ny);
39643       float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
39644 
39645       // Fill the first plane with function values
39646       Y = y0;
39647       cimg_forY(values1,y) {
39648         X = x0;
39649         cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
39650         Y+=dy;
39651       }
39652 
39653       // Run Marching Cubes algorithm
39654       Z = z0; nZ = Z + dz;
39655       for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
39656         Y = y0; nY = Y + dy;
39657         indices2.fill(-1);
39658         for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
39659           X = x0; nX = X + dx;
39660           for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
39661 
39662             // Determine cube configuration
39663             const float
39664               val0 = values1(xi,yi),
39665               val1 = values1(nxi,yi),
39666               val2 = values1(nxi,nyi),
39667               val3 = values1(xi,nyi),
39668               val4 = values2(xi,yi) = (float)func(X,Y,nZ),
39669               val5 = values2(nxi,yi) = (float)func(nX,Y,nZ),
39670               val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
39671               val7 = values2(xi,nyi) = (float)func(X,nY,nZ);
39672 
39673             const unsigned int configuration =
39674               (val0<isovalue?1U:0U)  | (val1<isovalue?2U:0U)  | (val2<isovalue?4U:0U)  | (val3<isovalue?8U:0U) |
39675               (val4<isovalue?16U:0U) | (val5<isovalue?32U:0U) | (val6<isovalue?64U:0U) | (val7<isovalue?128U:0U),
39676               edge = edges[configuration];
39677 
39678             // Compute intersection vertices
39679             if (edge) {
39680               if ((edge&1) && indices1(xi,yi,0)<0) {
39681                 const float Xi = X + (isovalue-val0)*dx/(val1-val0);
39682                 indices1(xi,yi,0) = vertices.width();
39683                 CImg<floatT>::vector(Xi,Y,Z).move_to(vertices);
39684               }
39685               if ((edge&2) && indices1(nxi,yi,1)<0) {
39686                 const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
39687                 indices1(nxi,yi,1) = vertices.width();
39688                 CImg<floatT>::vector(nX,Yi,Z).move_to(vertices);
39689               }
39690               if ((edge&4) && indices1(xi,nyi,0)<0) {
39691                 const float Xi = X + (isovalue-val3)*dx/(val2-val3);
39692                 indices1(xi,nyi,0) = vertices.width();
39693                 CImg<floatT>::vector(Xi,nY,Z).move_to(vertices);
39694               }
39695               if ((edge&8) && indices1(xi,yi,1)<0) {
39696                 const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
39697                 indices1(xi,yi,1) = vertices.width();
39698                 CImg<floatT>::vector(X,Yi,Z).move_to(vertices);
39699               }
39700               if ((edge&16) && indices2(xi,yi,0)<0) {
39701                 const float Xi = X + (isovalue-val4)*dx/(val5-val4);
39702                 indices2(xi,yi,0) = vertices.width();
39703                 CImg<floatT>::vector(Xi,Y,nZ).move_to(vertices);
39704               }
39705               if ((edge&32) && indices2(nxi,yi,1)<0) {
39706                 const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
39707                 indices2(nxi,yi,1) = vertices.width();
39708                 CImg<floatT>::vector(nX,Yi,nZ).move_to(vertices);
39709               }
39710               if ((edge&64) && indices2(xi,nyi,0)<0) {
39711                 const float Xi = X + (isovalue-val7)*dx/(val6-val7);
39712                 indices2(xi,nyi,0) = vertices.width();
39713                 CImg<floatT>::vector(Xi,nY,nZ).move_to(vertices);
39714               }
39715               if ((edge&128) && indices2(xi,yi,1)<0)  {
39716                 const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
39717                 indices2(xi,yi,1) = vertices.width();
39718                 CImg<floatT>::vector(X,Yi,nZ).move_to(vertices);
39719               }
39720               if ((edge&256) && indices1(xi,yi,2)<0) {
39721                 const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
39722                 indices1(xi,yi,2) = vertices.width();
39723                 CImg<floatT>::vector(X,Y,Zi).move_to(vertices);
39724               }
39725               if ((edge&512) && indices1(nxi,yi,2)<0)  {
39726                 const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
39727                 indices1(nxi,yi,2) = vertices.width();
39728                 CImg<floatT>::vector(nX,Y,Zi).move_to(vertices);
39729               }
39730               if ((edge&1024) && indices1(nxi,nyi,2)<0) {
39731                 const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
39732                 indices1(nxi,nyi,2) = vertices.width();
39733                 CImg<floatT>::vector(nX,nY,Zi).move_to(vertices);
39734               }
39735               if ((edge&2048) && indices1(xi,nyi,2)<0) {
39736                 const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
39737                 indices1(xi,nyi,2) = vertices.width();
39738                 CImg<floatT>::vector(X,nY,Zi).move_to(vertices);
39739               }
39740 
39741               // Create triangles
39742               for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
39743                 const unsigned int
39744                   p0 = (unsigned int)*(triangle++),
39745                   p1 = (unsigned int)*(triangle++),
39746                   p2 = (unsigned int)*(triangle++);
39747                 const tf
39748                   i0 = (tf)(_isosurface3d_indice(p0,indices1,indices2,xi,yi,nxi,nyi)),
39749                   i1 = (tf)(_isosurface3d_indice(p1,indices1,indices2,xi,yi,nxi,nyi)),
39750                   i2 = (tf)(_isosurface3d_indice(p2,indices1,indices2,xi,yi,nxi,nyi));
39751                 CImg<tf>::vector(i0,i2,i1).move_to(primitives);
39752               }
39753             }
39754           }
39755         }
39756         cimg::swap(values1,values2);
39757         cimg::swap(indices1,indices2);
39758       }
39759       return vertices>'x';
39760     }
39761 
39762     //! Compute isosurface of a function, as a 3d object \overloading.
39763     template<typename tf>
39764     static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
39765                                      const float x0, const float y0, const float z0,
39766                                      const float x1, const float y1, const float z1,
39767                                      const int dx=32, const int dy=32, const int dz=32) {
39768       const _functor3d_expr func(expression);
39769       return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
39770     }
39771 
39772     template<typename t>
39773     static int _isosurface3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
39774                                     const unsigned int x, const unsigned int y,
39775                                     const unsigned int nx, const unsigned int ny) {
39776       switch (edge) {
39777       case 0 : return indices1(x,y,0);
39778       case 1 : return indices1(nx,y,1);
39779       case 2 : return indices1(x,ny,0);
39780       case 3 : return indices1(x,y,1);
39781       case 4 : return indices2(x,y,0);
39782       case 5 : return indices2(nx,y,1);
39783       case 6 : return indices2(x,ny,0);
39784       case 7 : return indices2(x,y,1);
39785       case 8 : return indices1(x,y,2);
39786       case 9 : return indices1(nx,y,2);
39787       case 10 : return indices1(nx,ny,2);
39788       case 11 : return indices1(x,ny,2);
39789       }
39790       return 0;
39791     }
39792 
39793     // Define functors for accessing image values (used in previous functions).
39794     struct _functor2d_int {
39795       const CImg<T>& ref;
39796       _functor2d_int(const CImg<T>& pref):ref(pref) {}
39797       float operator()(const float x, const float y) const {
39798         return (float)ref((int)x,(int)y);
39799       }
39800     };
39801 
39802     struct _functor2d_float {
39803       const CImg<T>& ref;
39804       _functor2d_float(const CImg<T>& pref):ref(pref) {}
39805       float operator()(const float x, const float y) const {
39806         return (float)ref._linear_atXY(x,y);
39807       }
39808     };
39809 
39810     struct _functor2d_expr {
39811       _cimg_math_parser *mp;
39812       ~_functor2d_expr() { mp->end(); delete mp; }
39813       _functor2d_expr(const char *const expr):mp(0) {
39814         mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
39815       }
39816       float operator()(const float x, const float y) const {
39817         return (float)(*mp)(x,y,0,0);
39818       }
39819     };
39820 
39821     struct _functor3d_int {
39822       const CImg<T>& ref;
39823       _functor3d_int(const CImg<T>& pref):ref(pref) {}
39824       float operator()(const float x, const float y, const float z) const {
39825         return (float)ref((int)x,(int)y,(int)z);
39826       }
39827     };
39828 
39829     struct _functor3d_float {
39830       const CImg<T>& ref;
39831       _functor3d_float(const CImg<T>& pref):ref(pref) {}
39832       float operator()(const float x, const float y, const float z) const {
39833         return (float)ref._linear_atXYZ(x,y,z);
39834       }
39835     };
39836 
39837     struct _functor3d_expr {
39838       _cimg_math_parser *mp;
39839       ~_functor3d_expr() { mp->end(); delete mp; }
39840       _functor3d_expr(const char *const expr):mp(0) {
39841         mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
39842       }
39843       float operator()(const float x, const float y, const float z) const {
39844         return (float)(*mp)(x,y,z,0);
39845       }
39846     };
39847 
39848     struct _functor4d_int {
39849       const CImg<T>& ref;
39850       _functor4d_int(const CImg<T>& pref):ref(pref) {}
39851       float operator()(const float x, const float y, const float z, const unsigned int c) const {
39852         return (float)ref((int)x,(int)y,(int)z,c);
39853       }
39854     };
39855 
39856     //! Generate a 3d box object.
39857     /**
39858        \param[out] primitives The returned list of the 3d object primitives
39859                               (template type \e tf should be at least \e unsigned \e int).
39860        \param size_x The width of the box (dimension along the X-axis).
39861        \param size_y The height of the box (dimension along the Y-axis).
39862        \param size_z The depth of the box (dimension along the Z-axis).
39863        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
39864        \par Example
39865        \code
39866        CImgList<unsigned int> faces3d;
39867        const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
39868        CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d);
39869        \endcode
39870        \image html ref_box3d.jpg
39871     **/
39872     template<typename tf>
39873     static CImg<floatT> box3d(CImgList<tf>& primitives,
39874                               const float size_x=200, const float size_y=100, const float size_z=100) {
39875       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);
39876       return CImg<floatT>(8,3,1,1,
39877                           0.,size_x,size_x,    0.,    0.,size_x,size_x,    0.,
39878                           0.,    0.,size_y,size_y,    0.,    0.,size_y,size_y,
39879                           0.,    0.,    0.,    0.,size_z,size_z,size_z,size_z);
39880     }
39881 
39882     //! Generate a 3d cone.
39883     /**
39884        \param[out] primitives The returned list of the 3d object primitives
39885                               (template type \e tf should be at least \e unsigned \e int).
39886        \param radius The radius of the cone basis.
39887        \param size_z The cone's height.
39888        \param subdivisions The number of basis angular subdivisions.
39889        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
39890        \par Example
39891        \code
39892        CImgList<unsigned int> faces3d;
39893        const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
39894        CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d);
39895        \endcode
39896        \image html ref_cone3d.jpg
39897     **/
39898     template<typename tf>
39899     static CImg<floatT> cone3d(CImgList<tf>& primitives,
39900                                const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
39901       primitives.assign();
39902       if (!subdivisions) return CImg<floatT>();
39903       CImgList<floatT> vertices(2,1,3,1,1,
39904                                 0.,0.,size_z,
39905                                 0.,0.,0.);
39906       for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
39907 	const float a = (float)(angle*cimg::PI/180);
39908         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
39909       }
39910       const unsigned int nbr = vertices._width - 2;
39911       for (unsigned int p = 0; p<nbr; ++p) {
39912 	const unsigned int curr = 2 + p, next = 2 + ((p + 1)%nbr);
39913         CImg<tf>::vector(1,next,curr).move_to(primitives);
39914         CImg<tf>::vector(0,curr,next).move_to(primitives);
39915       }
39916       return vertices>'x';
39917     }
39918 
39919     //! Generate a 3d cylinder.
39920     /**
39921        \param[out] primitives The returned list of the 3d object primitives
39922                               (template type \e tf should be at least \e unsigned \e int).
39923        \param radius The radius of the cylinder basis.
39924        \param size_z The cylinder's height.
39925        \param subdivisions The number of basis angular subdivisions.
39926        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
39927        \par Example
39928        \code
39929        CImgList<unsigned int> faces3d;
39930        const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
39931        CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d);
39932        \endcode
39933        \image html ref_cylinder3d.jpg
39934     **/
39935     template<typename tf>
39936     static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
39937                                    const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
39938       primitives.assign();
39939       if (!subdivisions) return CImg<floatT>();
39940       CImgList<floatT> vertices(2,1,3,1,1,
39941                                 0.,0.,0.,
39942                                 0.,0.,size_z);
39943       for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
39944 	const float a = (float)(angle*cimg::PI/180);
39945         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.0f).move_to(vertices);
39946         CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
39947       }
39948       const unsigned int nbr = (vertices._width - 2)/2;
39949       for (unsigned int p = 0; p<nbr; ++p) {
39950 	const unsigned int curr = 2 + 2*p, next = 2 + (2*((p + 1)%nbr));
39951         CImg<tf>::vector(0,next,curr).move_to(primitives);
39952         CImg<tf>::vector(1,curr + 1,next + 1).move_to(primitives);
39953         CImg<tf>::vector(curr,next,next + 1,curr + 1).move_to(primitives);
39954       }
39955       return vertices>'x';
39956     }
39957 
39958     //! Generate a 3d torus.
39959     /**
39960        \param[out] primitives The returned list of the 3d object primitives
39961                               (template type \e tf should be at least \e unsigned \e int).
39962        \param radius1 The large radius.
39963        \param radius2 The small radius.
39964        \param subdivisions1 The number of angular subdivisions for the large radius.
39965        \param subdivisions2 The number of angular subdivisions for the small radius.
39966        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
39967        \par Example
39968        \code
39969        CImgList<unsigned int> faces3d;
39970        const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
39971        CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d);
39972        \endcode
39973        \image html ref_torus3d.jpg
39974     **/
39975     template<typename tf>
39976     static CImg<floatT> torus3d(CImgList<tf>& primitives,
39977                                 const float radius1=100, const float radius2=30,
39978                                 const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
39979       primitives.assign();
39980       if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
39981       CImgList<floatT> vertices;
39982       for (unsigned int v = 0; v<subdivisions1; ++v) {
39983 	const float
39984 	  beta = (float)(v*2*cimg::PI/subdivisions1),
39985 	  xc = radius1*(float)std::cos(beta),
39986 	  yc = radius1*(float)std::sin(beta);
39987         for (unsigned int u = 0; u<subdivisions2; ++u) {
39988           const float
39989             alpha = (float)(u*2*cimg::PI/subdivisions2),
39990             x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
39991 	    y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
39992 	    z = radius2*(float)std::sin(alpha);
39993           CImg<floatT>::vector(x,y,z).move_to(vertices);
39994         }
39995       }
39996       for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
39997 	const unsigned int nv = (vv + 1)%subdivisions1;
39998         for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
39999           const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
40000           CImg<tf>::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives);
40001         }
40002       }
40003       return vertices>'x';
40004     }
40005 
40006     //! Generate a 3d XY-plane.
40007     /**
40008        \param[out] primitives The returned list of the 3d object primitives
40009                               (template type \e tf should be at least \e unsigned \e int).
40010        \param size_x The width of the plane (dimension along the X-axis).
40011        \param size_y The height of the plane (dimensions along the Y-axis).
40012        \param subdivisions_x The number of planar subdivisions along the X-axis.
40013        \param subdivisions_y The number of planar subdivisions along the Y-axis.
40014        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
40015        \par Example
40016        \code
40017        CImgList<unsigned int> faces3d;
40018        const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
40019        CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d);
40020        \endcode
40021        \image html ref_plane3d.jpg
40022     **/
40023     template<typename tf>
40024     static CImg<floatT> plane3d(CImgList<tf>& primitives,
40025                                 const float size_x=100, const float size_y=100,
40026                                 const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
40027       primitives.assign();
40028       if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
40029       CImgList<floatT> vertices;
40030       const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
40031       const float fx = (float)size_x/w, fy = (float)size_y/h;
40032       for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
40033         CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
40034       for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
40035         const int off1 = x + y*w, off2 = x + 1 + y*w, off3 = x + 1 + (y + 1)*w, off4 = x + (y + 1)*w;
40036 	CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
40037       }
40038       return vertices>'x';
40039     }
40040 
40041     //! Generate a 3d sphere.
40042     /**
40043        \param[out] primitives The returned list of the 3d object primitives
40044                               (template type \e tf should be at least \e unsigned \e int).
40045        \param radius The radius of the sphere (dimension along the X-axis).
40046        \param subdivisions The number of recursive subdivisions from an initial icosahedron.
40047        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
40048        \par Example
40049        \code
40050        CImgList<unsigned int> faces3d;
40051        const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
40052        CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d);
40053        \endcode
40054        \image html ref_sphere3d.jpg
40055     **/
40056     template<typename tf>
40057     static CImg<floatT> sphere3d(CImgList<tf>& primitives,
40058                                  const float radius=50, const unsigned int subdivisions=3) {
40059 
40060       // Create initial icosahedron
40061       primitives.assign();
40062       const double tmp = (1 + std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1 + tmp*tmp), b = tmp*a;
40063       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,
40064                                 -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.0,b,-a);
40065       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,
40066                         8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
40067                         5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
40068       // edge - length/2
40069       float he = (float)a;
40070 
40071       // Recurse subdivisions
40072       for (unsigned int i = 0; i<subdivisions; ++i) {
40073         const unsigned int L = primitives._width;
40074 	he/=2;
40075 	const float he2 = he*he;
40076         for (unsigned int l = 0; l<L; ++l) {
40077           const unsigned int
40078             p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
40079           const float
40080             x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
40081             x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
40082             x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
40083             tnx0 = (x0 + x1)/2, tny0 = (y0 + y1)/2, tnz0 = (z0 + z1)/2,
40084             nn0 = cimg::hypot(tnx0,tny0,tnz0),
40085             tnx1 = (x0 + x2)/2, tny1 = (y0 + y2)/2, tnz1 = (z0 + z2)/2,
40086             nn1 = cimg::hypot(tnx1,tny1,tnz1),
40087             tnx2 = (x1 + x2)/2, tny2 = (y1 + y2)/2, tnz2 = (z1 + z2)/2,
40088             nn2 = cimg::hypot(tnx2,tny2,tnz2),
40089             nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
40090             nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
40091             nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
40092           int i0 = -1, i1 = -1, i2 = -1;
40093           cimglist_for(vertices,p) {
40094             const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
40095             if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
40096             if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
40097             if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
40098           }
40099           if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; }
40100           if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; }
40101           if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; }
40102           primitives.remove(0);
40103           CImg<tf>::vector(p0,i0,i1).move_to(primitives);
40104           CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
40105           CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
40106           CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
40107         }
40108       }
40109       return (vertices>'x')*=radius;
40110     }
40111 
40112     //! Generate a 3d ellipsoid.
40113     /**
40114        \param[out] primitives The returned list of the 3d object primitives
40115                               (template type \e tf should be at least \e unsigned \e int).
40116        \param tensor The tensor which gives the shape and size of the ellipsoid.
40117        \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
40118        \return The N vertices (xi,yi,zi) of the 3d object as a Nx3 CImg<float> image (0<=i<=N - 1).
40119        \par Example
40120        \code
40121        CImgList<unsigned int> faces3d;
40122        const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
40123                          points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
40124        CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d);
40125        \endcode
40126        \image html ref_ellipsoid3d.jpg
40127     **/
40128     template<typename tf, typename t>
40129     static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
40130                                     const CImg<t>& tensor, const unsigned int subdivisions=3) {
40131       primitives.assign();
40132       if (!subdivisions) return CImg<floatT>();
40133       CImg<floatT> S, V;
40134       tensor.symmetric_eigen(S,V);
40135       const float orient =
40136         (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
40137         (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
40138         (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
40139       if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
40140       const float l0 = S[0], l1 = S[1], l2 = S[2];
40141       CImg<floatT> vertices = sphere3d(primitives,1.0,subdivisions);
40142       vertices.get_shared_row(0)*=l0;
40143       vertices.get_shared_row(1)*=l1;
40144       vertices.get_shared_row(2)*=l2;
40145       return V*vertices;
40146     }
40147 
40148     //! Convert 3d object into a CImg3d representation.
40149     /**
40150        \param primitives Primitives data of the 3d object.
40151        \param colors Colors data of the 3d object.
40152        \param opacities Opacities data of the 3d object.
40153        \param full_check Tells if full checking of the 3d object must be performed.
40154     **/
40155     template<typename tp, typename tc, typename to>
40156     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
40157                               const CImgList<tc>& colors,
40158                               const to& opacities,
40159                               const bool full_check=true) {
40160       return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this);
40161     }
40162 
40163     //! Convert 3d object into a CImg3d representation \overloading.
40164     template<typename tp, typename tc>
40165     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
40166                               const CImgList<tc>& colors,
40167                               const bool full_check=true) {
40168       return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this);
40169     }
40170 
40171     //! Convert 3d object into a CImg3d representation \overloading.
40172     template<typename tp>
40173     CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
40174                               const bool full_check=true) {
40175       return get_object3dtoCImg3d(primitives,full_check).move_to(*this);
40176     }
40177 
40178     //! Convert 3d object into a CImg3d representation \overloading.
40179     CImg<T>& object3dtoCImg3d(const bool full_check=true) {
40180       return get_object3dtoCImg3d(full_check).move_to(*this);
40181     }
40182 
40183     //! Convert 3d object into a CImg3d representation \newinstance.
40184     template<typename tp, typename tc, typename to>
40185     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
40186                                       const CImgList<tc>& colors,
40187                                       const to& opacities,
40188                                       const bool full_check=true) const {
40189       CImg<charT> error_message(1024);
40190       if (!is_object3d(primitives,colors,opacities,full_check,error_message))
40191         throw CImgInstanceException(_cimg_instance
40192                                     "object3dtoCImg3d(): Invalid specified 3d object (%u,%u) (%s).",
40193                                     cimg_instance,_width,primitives._width,error_message.data());
40194       CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
40195       float *ptrd = res._data;
40196 
40197       // Put magick number.
40198       *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
40199       *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
40200 
40201       // Put number of vertices and primitives.
40202       *(ptrd++) = cimg::uint2float(_width);
40203       *(ptrd++) = cimg::uint2float(primitives._width);
40204 
40205       // Put vertex data.
40206       if (is_empty() || !primitives) return res;
40207       const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
40208       cimg_forX(*this,p) {
40209         *(ptrd++) = (float)*(ptrx++);
40210         *(ptrd++) = (float)*(ptry++);
40211         *(ptrd++) = (float)*(ptrz++);
40212       }
40213 
40214       // Put primitive data.
40215       cimglist_for(primitives,p) {
40216         *(ptrd++) = (float)primitives[p].size();
40217         const tp *ptrp = primitives[p]._data;
40218         cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
40219       }
40220 
40221       // Put color/texture data.
40222       const unsigned int csiz = std::min(colors._width,primitives._width);
40223       for (int c = 0; c<(int)csiz; ++c) {
40224         const CImg<tc>& color = colors[c];
40225         const tc *ptrc = color._data;
40226         if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
40227         else {
40228           *(ptrd++) = -128.0f;
40229           int shared_ind = -1;
40230           if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
40231           if (shared_ind<0) {
40232             *(ptrd++) = (float)color._width;
40233             *(ptrd++) = (float)color._height;
40234             *(ptrd++) = (float)color._spectrum;
40235             cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
40236           } else {
40237             *(ptrd++) = (float)shared_ind;
40238             *(ptrd++) = 0;
40239             *(ptrd++) = 0;
40240           }
40241         }
40242       }
40243       const int csiz2 = primitives.width() - colors.width();
40244       for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; }
40245 
40246       // Put opacity data.
40247       ptrd = _object3dtoCImg3d(opacities,ptrd);
40248       const float *ptre = res.end();
40249       while (ptrd<ptre) *(ptrd++) = 1.0f;
40250       return res;
40251     }
40252 
40253     template<typename to>
40254     float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
40255       cimglist_for(opacities,o) {
40256         const CImg<to>& opacity = opacities[o];
40257         const to *ptro = opacity._data;
40258         if (opacity.size()==1) *(ptrd++) = (float)*ptro;
40259         else {
40260           *(ptrd++) = -128.0f;
40261           int shared_ind = -1;
40262           if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
40263           if (shared_ind<0) {
40264             *(ptrd++) = (float)opacity._width;
40265             *(ptrd++) = (float)opacity._height;
40266             *(ptrd++) = (float)opacity._spectrum;
40267             cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
40268           } else {
40269             *(ptrd++) = (float)shared_ind;
40270             *(ptrd++) = 0;
40271             *(ptrd++) = 0;
40272           }
40273         }
40274       }
40275       return ptrd;
40276     }
40277 
40278     template<typename to>
40279     float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
40280       const to *ptro = opacities._data;
40281       cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
40282       return ptrd;
40283     }
40284 
40285     template<typename tp, typename tc, typename to>
40286     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
40287                                         const CImgList<tc>& colors,
40288                                         const CImgList<to>& opacities) const {
40289       unsigned int siz = 8U + 3*_width;
40290       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
40291       for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
40292         if (colors[c].is_shared()) siz+=4;
40293         else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; }
40294       }
40295       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
40296       cimglist_for(opacities,o) {
40297         if (opacities[o].is_shared()) siz+=4;
40298         else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4 + osiz:1; }
40299       }
40300       siz+=primitives._width - opacities._width;
40301       return siz;
40302     }
40303 
40304     template<typename tp, typename tc, typename to>
40305     unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
40306                                         const CImgList<tc>& colors,
40307                                         const CImg<to>& opacities) const {
40308       unsigned int siz = 8U + 3*_width;
40309       cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
40310       for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
40311         const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3;
40312       }
40313       if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
40314       siz+=primitives.size();
40315       cimg::unused(opacities);
40316       return siz;
40317     }
40318 
40319     //! Convert 3d object into a CImg3d representation \overloading.
40320     template<typename tp, typename tc>
40321     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
40322                                       const CImgList<tc>& colors,
40323                                       const bool full_check=true) const {
40324       CImgList<T> opacities;
40325       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
40326     }
40327 
40328     //! Convert 3d object into a CImg3d representation \overloading.
40329     template<typename tp>
40330     CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
40331                                       const bool full_check=true) const {
40332       CImgList<T> colors, opacities;
40333       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
40334     }
40335 
40336     //! Convert 3d object into a CImg3d representation \overloading.
40337     CImg<floatT> get_object3dtoCImg3d(const bool full_check=true) const {
40338       CImgList<T> opacities, colors;
40339       CImgList<uintT> primitives(width(),1,1,1,1);
40340       cimglist_for(primitives,p) primitives(p,0) = p;
40341       return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
40342     }
40343 
40344     //! Convert CImg3d representation into a 3d object.
40345     /**
40346        \param[out] primitives Primitives data of the 3d object.
40347        \param[out] colors Colors data of the 3d object.
40348        \param[out] opacities Opacities data of the 3d object.
40349        \param full_check Tells if full checking of the 3d object must be performed.
40350     **/
40351     template<typename tp, typename tc, typename to>
40352     CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives,
40353                               CImgList<tc>& colors,
40354                               CImgList<to>& opacities,
40355                               const bool full_check=true) {
40356       return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this);
40357     }
40358 
40359     //! Convert CImg3d representation into a 3d object \newinstance.
40360     template<typename tp, typename tc, typename to>
40361     CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives,
40362                                  CImgList<tc>& colors,
40363                                  CImgList<to>& opacities,
40364                                  const bool full_check=true) const {
40365       CImg<charT> error_message(1024);
40366       if (!is_CImg3d(full_check,error_message))
40367         throw CImgInstanceException(_cimg_instance
40368                                     "CImg3dtoobject3d(): image instance is not a CImg3d (%s).",
40369                                     cimg_instance,error_message.data());
40370       const T *ptrs = _data + 6;
40371       const unsigned int
40372         nb_points = cimg::float2uint((float)*(ptrs++)),
40373         nb_primitives = cimg::float2uint((float)*(ptrs++));
40374       const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
40375       ptrs+=3*nb_points;
40376       primitives.assign(nb_primitives);
40377       cimglist_for(primitives,p) {
40378         const unsigned int nb_inds = (unsigned int)*(ptrs++);
40379         primitives[p].assign(1,nb_inds);
40380         tp *ptrp = primitives[p]._data;
40381         for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
40382       }
40383       colors.assign(nb_primitives);
40384       cimglist_for(colors,c) {
40385         if (*ptrs==(T)-128) {
40386           ++ptrs;
40387           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
40388           if (!h && !s) colors[c].assign(colors[w],true);
40389           else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
40390         } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
40391       }
40392       opacities.assign(nb_primitives);
40393       cimglist_for(opacities,o) {
40394         if (*ptrs==(T)-128) {
40395           ++ptrs;
40396           const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
40397           if (!h && !s) opacities[o].assign(opacities[w],true);
40398           else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
40399         } else opacities[o].assign(1,1,1,1,*(ptrs++));
40400       }
40401       return points;
40402     }
40403 
40404     //@}
40405     //---------------------------
40406     //
40407     //! \name Drawing Functions
40408     //@{
40409     //---------------------------
40410 
40411 #define cimg_init_scanline(color,opacity) \
40412     const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.0f); \
40413     const ulongT _sc_whd = (ulongT)_width*_height*_depth
40414 
40415 #define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \
40416     _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd)
40417 
40418     // [internal] The following _draw_scanline() routines are *non user-friendly functions*,
40419     // used only for internal purpose.
40420     // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid.
40421     template<typename tc>
40422     CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
40423                             const tc *const color, const float opacity,
40424                             const float brightness,
40425                             const float nopacity, const float copacity, const ulongT whd) {
40426       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
40427       const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width() - 1, dx = nx1 - nx0;
40428       if (dx>=0) {
40429         const tc *col = color;
40430         const ulongT off = whd - dx - 1;
40431         T *ptrd = data(nx0,y);
40432         if (opacity>=1) { // ** Opaque drawing **
40433           if (brightness==1) { // Brightness==1
40434             if (sizeof(T)!=1) cimg_forC(*this,c) {
40435                 const T val = (T)*(col++);
40436                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
40437                 ptrd+=off;
40438               } else cimg_forC(*this,c) {
40439                 const T val = (T)*(col++);
40440                 std::memset(ptrd,(int)val,dx + 1);
40441                 ptrd+=whd;
40442               }
40443           } else if (brightness<1) { // Brightness<1
40444             if (sizeof(T)!=1) cimg_forC(*this,c) {
40445                 const T val = (T)(*(col++)*brightness);
40446                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
40447                 ptrd+=off;
40448               } else cimg_forC(*this,c) {
40449                 const T val = (T)(*(col++)*brightness);
40450                 std::memset(ptrd,(int)val,dx + 1);
40451                 ptrd+=whd;
40452               }
40453           } else { // Brightness>1
40454             if (sizeof(T)!=1) cimg_forC(*this,c) {
40455                 const T val = (T)((2-brightness)**(col++) + (brightness - 1)*maxval);
40456                 for (int x = dx; x>=0; --x) *(ptrd++) = val;
40457                 ptrd+=off;
40458               } else cimg_forC(*this,c) {
40459                 const T val = (T)((2-brightness)**(col++) + (brightness - 1)*maxval);
40460                 std::memset(ptrd,(int)val,dx + 1);
40461                 ptrd+=whd;
40462               }
40463           }
40464         } else { // ** Transparent drawing **
40465           if (brightness==1) { // Brightness==1
40466             cimg_forC(*this,c) {
40467               const Tfloat val = *(col++)*nopacity;
40468               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
40469               ptrd+=off;
40470             }
40471           } else if (brightness<=1) { // Brightness<1
40472             cimg_forC(*this,c) {
40473               const Tfloat val = *(col++)*brightness*nopacity;
40474               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
40475               ptrd+=off;
40476             }
40477           } else { // Brightness>1
40478             cimg_forC(*this,c) {
40479               const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*maxval)*nopacity;
40480               for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
40481               ptrd+=off;
40482             }
40483           }
40484         }
40485       }
40486       return *this;
40487     }
40488 
40489     //! Draw a 3d point.
40490     /**
40491        \param x0 X-coordinate of the point.
40492        \param y0 Y-coordinate of the point.
40493        \param z0 Z-coordinate of the point.
40494        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
40495        \param opacity Drawing opacity.
40496        \note
40497        - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
40498        \par Example:
40499        \code
40500        CImg<unsigned char> img(100,100,1,3,0);
40501        const unsigned char color[] = { 255,128,64 };
40502        img.draw_point(50,50,color);
40503        \endcode
40504     **/
40505     template<typename tc>
40506     CImg<T>& draw_point(const int x0, const int y0, const int z0,
40507                         const tc *const color, const float opacity=1) {
40508       if (is_empty()) return *this;
40509       if (!color)
40510         throw CImgArgumentException(_cimg_instance
40511                                     "draw_point(): Specified color is (null).",
40512                                     cimg_instance);
40513       if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
40514         const ulongT whd = (ulongT)_width*_height*_depth;
40515         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
40516         T *ptrd = data(x0,y0,z0,0);
40517         const tc *col = color;
40518         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
40519         else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
40520       }
40521       return *this;
40522     }
40523 
40524     //! Draw a 2d point \simplification.
40525     template<typename tc>
40526     CImg<T>& draw_point(const int x0, const int y0,
40527                         const tc *const color, const float opacity=1) {
40528       return draw_point(x0,y0,0,color,opacity);
40529     }
40530 
40531     // Draw a points cloud.
40532     /**
40533        \param points Image of vertices coordinates.
40534        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
40535        \param opacity Drawing opacity.
40536     **/
40537     template<typename t, typename tc>
40538     CImg<T>& draw_point(const CImg<t>& points,
40539                         const tc *const color, const float opacity=1) {
40540       if (is_empty() || !points) return *this;
40541       switch (points._height) {
40542       case 0 : case 1 :
40543         throw CImgArgumentException(_cimg_instance
40544                                     "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).",
40545                                     cimg_instance,
40546                                     points._width,points._height,points._depth,points._spectrum,points._data);
40547       case 2 : {
40548         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
40549       } break;
40550       default : {
40551         cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
40552       }
40553       }
40554       return *this;
40555     }
40556 
40557     //! Draw a 2d line.
40558     /**
40559        \param x0 X-coordinate of the starting line point.
40560        \param y0 Y-coordinate of the starting line point.
40561        \param x1 X-coordinate of the ending line point.
40562        \param y1 Y-coordinate of the ending line point.
40563        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
40564        \param opacity Drawing opacity.
40565        \param pattern An integer whose bits describe the line pattern.
40566        \param init_hatch Tells if a reinitialization of the hash state must be done.
40567        \note
40568        - Line routine uses Bresenham's algorithm.
40569        - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
40570        \par Example:
40571        \code
40572        CImg<unsigned char> img(100,100,1,3,0);
40573        const unsigned char color[] = { 255,128,64 };
40574         img.draw_line(40,40,80,70,color);
40575        \endcode
40576     **/
40577     template<typename tc>
40578     CImg<T>& draw_line(const int x0, const int y0,
40579                        const int x1, const int y1,
40580                        const tc *const color, const float opacity=1,
40581                        const unsigned int pattern=~0U, const bool init_hatch=true) {
40582       if (is_empty()) return *this;
40583       if (!color)
40584         throw CImgArgumentException(_cimg_instance
40585                                     "draw_line(): Specified color is (null).",
40586                                     cimg_instance);
40587       static unsigned int hatch = ~0U - (~0U>>1);
40588       if (init_hatch) hatch = ~0U - (~0U>>1);
40589       const bool xdir = x0<x1, ydir = y0<y1;
40590       int
40591 	nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
40592 	&xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
40593         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
40594 	&xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
40595         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
40596       if (xright<0 || xleft>=width()) return *this;
40597       if (xleft<0) { yleft-=(int)((float)xleft*((float)yright - yleft)/((float)xright - xleft)); xleft = 0; }
40598       if (xright>=width()) {
40599         yright-=(int)(((float)xright - width())*((float)yright - yleft)/((float)xright - xleft));
40600         xright = width() - 1;
40601       }
40602       if (ydown<0 || yup>=height()) return *this;
40603       if (yup<0) { xup-=(int)((float)yup*((float)xdown - xup)/((float)ydown - yup)); yup = 0; }
40604       if (ydown>=height()) {
40605         xdown-=(int)(((float)ydown - height())*((float)xdown - xup)/((float)ydown - yup));
40606         ydown = height() - 1;
40607       }
40608       T *ptrd0 = data(nx0,ny0);
40609       int dx = xright - xleft, dy = ydown - yup;
40610       const bool steep = dy>dx;
40611       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
40612       const longT
40613         offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
40614         offy = (longT)(ny0<ny1?1:-1)*(steep?1:width());
40615       const ulongT wh = (ulongT)_width*_height;
40616       if (opacity>=1) {
40617         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
40618           if (pattern&hatch) {
40619             T *ptrd = ptrd0; const tc* col = color;
40620             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
40621           }
40622           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
40623           ptrd0+=offx;
40624           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
40625         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
40626           T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
40627           ptrd0+=offx;
40628           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
40629         }
40630       } else {
40631         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
40632         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
40633           if (pattern&hatch) {
40634             T *ptrd = ptrd0; const tc* col = color;
40635             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
40636           }
40637           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
40638           ptrd0+=offx;
40639           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
40640         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
40641           T *ptrd = ptrd0; const tc* col = color;
40642           cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
40643           ptrd0+=offx;
40644           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
40645         }
40646       }
40647       return *this;
40648     }
40649 
40650     //! Draw a 2d line, with z-buffering.
40651     /**
40652        \param zbuffer Zbuffer image.
40653        \param x0 X-coordinate of the starting point.
40654        \param y0 Y-coordinate of the starting point.
40655        \param z0 Z-coordinate of the starting point
40656        \param x1 X-coordinate of the ending point.
40657        \param y1 Y-coordinate of the ending point.
40658        \param z1 Z-coordinate of the ending point.
40659        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
40660        \param opacity Drawing opacity.
40661        \param pattern An integer whose bits describe the line pattern.
40662        \param init_hatch Tells if a reinitialization of the hash state must be done.
40663     **/
40664     template<typename tz,typename tc>
40665     CImg<T>& draw_line(CImg<tz>& zbuffer,
40666                        const int x0, const int y0, const float z0,
40667                        const int x1, const int y1, const float z1,
40668                        const tc *const color, const float opacity=1,
40669                        const unsigned int pattern=~0U, const bool init_hatch=true) {
40670       typedef typename cimg::superset<tz,float>::type tzfloat;
40671       if (is_empty() || z0<=0 || z1<=0) return *this;
40672       if (!color)
40673         throw CImgArgumentException(_cimg_instance
40674                                     "draw_line(): Specified color is (null).",
40675                                     cimg_instance);
40676       if (!is_sameXY(zbuffer))
40677         throw CImgArgumentException(_cimg_instance
40678                                     "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
40679                                     "different dimensions.",
40680                                     cimg_instance,
40681                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
40682       static unsigned int hatch = ~0U - (~0U>>1);
40683       if (init_hatch) hatch = ~0U - (~0U>>1);
40684       const bool xdir = x0<x1, ydir = y0<y1;
40685       int
40686         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
40687         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
40688         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
40689         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
40690         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
40691       tzfloat
40692         Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1, nz0 = Z0, nz1 = Z1, dz = Z1 - Z0,
40693         &zleft = xdir?nz0:nz1,
40694         &zright = xdir?nz1:nz0,
40695         &zup = ydir?nz0:nz1,
40696         &zdown = ydir?nz1:nz0;
40697       if (xright<0 || xleft>=width()) return *this;
40698       if (xleft<0) {
40699         const float D = (float)xright - xleft;
40700         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
40701         zleft-=(tzfloat)xleft*(zright - zleft)/D;
40702         xleft = 0;
40703       }
40704       if (xright>=width()) {
40705         const float d = (float)xright - width(), D = (float)xright - xleft;
40706         yright-=(int)(d*((float)yright - yleft)/D);
40707         zright-=(tzfloat)d*(zright - zleft)/D;
40708         xright = width() - 1;
40709       }
40710       if (ydown<0 || yup>=height()) return *this;
40711       if (yup<0) {
40712         const float D = (float)ydown - yup;
40713         xup-=(int)((float)yup*((float)xdown - xup)/D);
40714         zup-=(tzfloat)yup*(zdown - zup)/D;
40715         yup = 0;
40716       }
40717       if (ydown>=height()) {
40718         const float d = (float)ydown - height(), D = (float)ydown - yup;
40719         xdown-=(int)(d*((float)xdown - xup)/D);
40720         zdown-=(tzfloat)d*(zdown - zup)/D;
40721         ydown = height() - 1;
40722       }
40723       T *ptrd0 = data(nx0,ny0);
40724       tz *ptrz = zbuffer.data(nx0,ny0);
40725       int dx = xright - xleft, dy = ydown - yup;
40726       const bool steep = dy>dx;
40727       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
40728       const longT
40729         offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
40730         offy = (longT)(ny0<ny1?1:-1)*(steep?1:width());
40731       const ulongT
40732         wh = (ulongT)_width*_height,
40733         ndx = (ulongT)(dx>0?dx:1);
40734       if (opacity>=1) {
40735         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
40736           const tzfloat z = Z0 + x*dz/ndx;
40737           if (z>=(tzfloat)*ptrz && pattern&hatch) {
40738             *ptrz = (tz)z;
40739             T *ptrd = ptrd0; const tc *col = color;
40740             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
40741           }
40742           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
40743           ptrd0+=offx; ptrz+=offx;
40744           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
40745         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
40746           const tzfloat z = Z0 + x*dz/ndx;
40747           if (z>=(tzfloat)*ptrz) {
40748             *ptrz = (tz)z;
40749             T *ptrd = ptrd0; const tc *col = color;
40750             cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
40751           }
40752           ptrd0+=offx; ptrz+=offx;
40753           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
40754         }
40755       } else {
40756         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
40757         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
40758           const tzfloat z = Z0 + x*dz/ndx;
40759           if (z>=(tzfloat)*ptrz && pattern&hatch) {
40760             *ptrz = (tz)z;
40761             T *ptrd = ptrd0; const tc *col = color;
40762             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
40763           }
40764           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
40765           ptrd0+=offx; ptrz+=offx;
40766           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
40767         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
40768           const tzfloat z = Z0 + x*dz/ndx;
40769           if (z>=(tzfloat)*ptrz) {
40770             *ptrz = (tz)z;
40771             T *ptrd = ptrd0; const tc *col = color;
40772             cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
40773           }
40774           ptrd0+=offx; ptrz+=offx;
40775           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
40776         }
40777       }
40778       return *this;
40779     }
40780 
40781     //! Draw a 3d line.
40782     /**
40783        \param x0 X-coordinate of the starting point.
40784        \param y0 Y-coordinate of the starting point.
40785        \param z0 Z-coordinate of the starting point
40786        \param x1 X-coordinate of the ending point.
40787        \param y1 Y-coordinate of the ending point.
40788        \param z1 Z-coordinate of the ending point.
40789        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
40790        \param opacity Drawing opacity.
40791        \param pattern An integer whose bits describe the line pattern.
40792        \param init_hatch Tells if a reinitialization of the hash state must be done.
40793     **/
40794     template<typename tc>
40795     CImg<T>& draw_line(const int x0, const int y0, const int z0,
40796                        const int x1, const int y1, const int z1,
40797                        const tc *const color, const float opacity=1,
40798                        const unsigned int pattern=~0U, const bool init_hatch=true) {
40799       if (is_empty()) return *this;
40800       if (!color)
40801         throw CImgArgumentException(_cimg_instance
40802                                     "draw_line(): Specified color is (null).",
40803                                     cimg_instance);
40804       static unsigned int hatch = ~0U - (~0U>>1);
40805       if (init_hatch) hatch = ~0U - (~0U>>1);
40806       int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1;
40807       if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
40808       if (nx1<0 || nx0>=width()) return *this;
40809       if (nx0<0) {
40810         const float D = 1.0f + nx1 - nx0;
40811         ny0-=(int)((float)nx0*(1.0f + ny1 - ny0)/D);
40812         nz0-=(int)((float)nx0*(1.0f + nz1 - nz0)/D);
40813         nx0 = 0;
40814       }
40815       if (nx1>=width()) {
40816         const float d = (float)nx1 - width(), D = 1.0f + nx1 - nx0;
40817         ny1+=(int)(d*(1.0f + ny0 - ny1)/D);
40818         nz1+=(int)(d*(1.0f + nz0 - nz1)/D);
40819         nx1 = width() - 1;
40820       }
40821       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
40822       if (ny1<0 || ny0>=height()) return *this;
40823       if (ny0<0) {
40824         const float D = 1.0f + ny1 - ny0;
40825         nx0-=(int)((float)ny0*(1.0f + nx1 - nx0)/D);
40826         nz0-=(int)((float)ny0*(1.0f + nz1 - nz0)/D);
40827         ny0 = 0;
40828       }
40829       if (ny1>=height()) {
40830         const float d = (float)ny1 - height(), D = 1.0f + ny1 - ny0;
40831         nx1+=(int)(d*(1.0f + nx0 - nx1)/D);
40832         nz1+=(int)(d*(1.0f + nz0 - nz1)/D);
40833         ny1 = height() - 1;
40834       }
40835       if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
40836       if (nz1<0 || nz0>=depth()) return *this;
40837       if (nz0<0) {
40838         const float D = 1.0f + nz1 - nz0;
40839         nx0-=(int)((float)nz0*(1.0f + nx1 - nx0)/D);
40840         ny0-=(int)((float)nz0*(1.0f + ny1 - ny0)/D);
40841         nz0 = 0;
40842       }
40843       if (nz1>=depth()) {
40844         const float d = (float)nz1 - depth(), D = 1.0f + nz1 - nz0;
40845         nx1+=(int)(d*(1.0f + nx0 - nx1)/D);
40846         ny1+=(int)(d*(1.0f + ny0 - ny1)/D);
40847         nz1 = depth() - 1;
40848       }
40849       const unsigned int dmax = (unsigned int)cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0);
40850       const ulongT whd = (ulongT)_width*_height*_depth;
40851       const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax;
40852       float x = (float)nx0, y = (float)ny0, z = (float)nz0;
40853       if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) {
40854         if (!(~pattern) || (~pattern && pattern&hatch)) {
40855           T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
40856           const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
40857         }
40858         x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
40859       } else {
40860         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
40861         for (unsigned int t = 0; t<=dmax; ++t) {
40862           if (!(~pattern) || (~pattern && pattern&hatch)) {
40863             T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
40864             const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
40865           }
40866           x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
40867         }
40868       }
40869       return *this;
40870     }
40871 
40872     //! Draw a textured 2d line.
40873     /**
40874        \param x0 X-coordinate of the starting line point.
40875        \param y0 Y-coordinate of the starting line point.
40876        \param x1 X-coordinate of the ending line point.
40877        \param y1 Y-coordinate of the ending line point.
40878        \param texture Texture image defining the pixel colors.
40879        \param tx0 X-coordinate of the starting texture point.
40880        \param ty0 Y-coordinate of the starting texture point.
40881        \param tx1 X-coordinate of the ending texture point.
40882        \param ty1 Y-coordinate of the ending texture point.
40883        \param opacity Drawing opacity.
40884        \param pattern An integer whose bits describe the line pattern.
40885        \param init_hatch Tells if the hash variable must be reinitialized.
40886        \note
40887        - Line routine uses the well known Bresenham's algorithm.
40888        \par Example:
40889        \code
40890        CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
40891        const unsigned char color[] = { 255,128,64 };
40892        img.draw_line(40,40,80,70,texture,0,0,255,255);
40893        \endcode
40894     **/
40895     template<typename tc>
40896     CImg<T>& draw_line(const int x0, const int y0,
40897                        const int x1, const int y1,
40898                        const CImg<tc>& texture,
40899                        const int tx0, const int ty0,
40900                        const int tx1, const int ty1,
40901                        const float opacity=1,
40902                        const unsigned int pattern=~0U, const bool init_hatch=true) {
40903       if (is_empty()) return *this;
40904       if (texture._depth>1 || texture._spectrum<_spectrum)
40905         throw CImgArgumentException(_cimg_instance
40906                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
40907                                     cimg_instance,
40908                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
40909       if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
40910       static unsigned int hatch = ~0U - (~0U>>1);
40911       if (init_hatch) hatch = ~0U - (~0U>>1);
40912       const bool xdir = x0<x1, ydir = y0<y1;
40913       int
40914         dtx = tx1-tx0, dty = ty1-ty0,
40915         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
40916         tnx0 = tx0, tnx1 = tx1, tny0 = ty0, tny1 = ty1,
40917         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
40918         &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
40919         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0,
40920         &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
40921       if (xright<0 || xleft>=width()) return *this;
40922       if (xleft<0) {
40923         const float D = (float)xright - xleft;
40924         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
40925         txleft-=(int)((float)xleft*((float)txright - txleft)/D);
40926         tyleft-=(int)((float)xleft*((float)tyright - tyleft)/D);
40927         xleft = 0;
40928       }
40929       if (xright>=width()) {
40930         const float d = (float)xright - width(), D = (float)xright - xleft;
40931         yright-=(int)(d*((float)yright - yleft)/D);
40932         txright-=(int)(d*((float)txright - txleft)/D);
40933         tyright-=(int)(d*((float)tyright - tyleft)/D);
40934         xright = width() - 1;
40935       }
40936       if (ydown<0 || yup>=height()) return *this;
40937       if (yup<0) {
40938         const float D = (float)ydown - yup;
40939         xup-=(int)((float)yup*((float)xdown - xup)/D);
40940         txup-=(int)((float)yup*((float)txdown - txup)/D);
40941         tyup-=(int)((float)yup*((float)tydown - tyup)/D);
40942         yup = 0;
40943       }
40944       if (ydown>=height()) {
40945         const float d = (float)ydown - height(), D = (float)ydown - yup;
40946         xdown-=(int)(d*((float)xdown - xup)/D);
40947         txdown-=(int)(d*((float)txdown - txup)/D);
40948         tydown-=(int)(d*((float)tydown - tyup)/D);
40949         ydown = height() - 1;
40950       }
40951       T *ptrd0 = data(nx0,ny0);
40952       int dx = xright - xleft, dy = ydown - yup;
40953       const bool steep = dy>dx;
40954       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
40955       const longT
40956         offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
40957         offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()),
40958         ndx = (longT)(dx>0?dx:1);
40959       const ulongT
40960         whd = (ulongT)_width*_height*_depth,
40961         twh = (ulongT)texture._width*texture._height;
40962 
40963       if (opacity>=1) {
40964         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
40965           if (pattern&hatch) {
40966             T *ptrd = ptrd0;
40967             const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
40968             const tc *col = &texture._atXY(tx,ty);
40969             cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
40970           }
40971           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
40972           ptrd0+=offx;
40973           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
40974         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
40975           T *ptrd = ptrd0;
40976           const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
40977           const tc *col = &texture._atXY(tx,ty);
40978           cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
40979           ptrd0+=offx;
40980           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
40981         }
40982       } else {
40983         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
40984         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
40985           T *ptrd = ptrd0;
40986           if (pattern&hatch) {
40987             const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
40988             const tc *col = &texture._atXY(tx,ty);
40989             cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
40990           }
40991           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
40992           ptrd0+=offx;
40993           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
40994         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
40995           T *ptrd = ptrd0;
40996           const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
40997           const tc *col = &texture._atXY(tx,ty);
40998           cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
40999           ptrd0+=offx;
41000           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
41001         }
41002       }
41003       return *this;
41004     }
41005 
41006     //! Draw a textured 2d line, with perspective correction.
41007     /**
41008        \param x0 X-coordinate of the starting point.
41009        \param y0 Y-coordinate of the starting point.
41010        \param z0 Z-coordinate of the starting point
41011        \param x1 X-coordinate of the ending point.
41012        \param y1 Y-coordinate of the ending point.
41013        \param z1 Z-coordinate of the ending point.
41014        \param texture Texture image defining the pixel colors.
41015        \param tx0 X-coordinate of the starting texture point.
41016        \param ty0 Y-coordinate of the starting texture point.
41017        \param tx1 X-coordinate of the ending texture point.
41018        \param ty1 Y-coordinate of the ending texture point.
41019        \param opacity Drawing opacity.
41020        \param pattern An integer whose bits describe the line pattern.
41021        \param init_hatch Tells if the hash variable must be reinitialized.
41022     **/
41023     template<typename tc>
41024     CImg<T>& draw_line(const int x0, const int y0, const float z0,
41025                        const int x1, const int y1, const float z1,
41026                        const CImg<tc>& texture,
41027                        const int tx0, const int ty0,
41028                        const int tx1, const int ty1,
41029                        const float opacity=1,
41030                        const unsigned int pattern=~0U, const bool init_hatch=true) {
41031       if (is_empty() && z0<=0 && z1<=0) return *this;
41032       if (texture._depth>1 || texture._spectrum<_spectrum)
41033         throw CImgArgumentException(_cimg_instance
41034                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
41035                                     cimg_instance,
41036                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
41037       if (is_overlapped(texture))
41038         return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
41039       static unsigned int hatch = ~0U - (~0U>>1);
41040       if (init_hatch) hatch = ~0U - (~0U>>1);
41041       const bool xdir = x0<x1, ydir = y0<y1;
41042       int
41043         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
41044         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
41045         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
41046         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
41047         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
41048       float
41049         Tx0 = tx0/z0, Tx1 = tx1/z1,
41050         Ty0 = ty0/z0, Ty1 = ty1/z1,
41051         Z0 = 1/z0, Z1 = 1/z1,
41052         dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
41053         tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
41054         &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
41055         &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
41056         &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
41057         &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
41058       if (xright<0 || xleft>=width()) return *this;
41059       if (xleft<0) {
41060         const float D = (float)xright - xleft;
41061         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
41062         zleft-=(float)xleft*(zright - zleft)/D;
41063         txleft-=(float)xleft*(txright - txleft)/D;
41064         tyleft-=(float)xleft*(tyright - tyleft)/D;
41065         xleft = 0;
41066       }
41067       if (xright>=width()) {
41068         const float d = (float)xright - width(), D = (float)xright - xleft;
41069         yright-=(int)(d*((float)yright - yleft)/D);
41070         zright-=d*(zright - zleft)/D;
41071         txright-=d*(txright - txleft)/D;
41072         tyright-=d*(tyright - tyleft)/D;
41073         xright = width() - 1;
41074       }
41075       if (ydown<0 || yup>=height()) return *this;
41076       if (yup<0) {
41077         const float D = (float)ydown - yup;
41078         xup-=(int)((float)yup*((float)xdown - xup)/D);
41079         zup-=(float)yup*(zdown - zup)/D;
41080         txup-=(float)yup*(txdown - txup)/D;
41081         tyup-=(float)yup*(tydown - tyup)/D;
41082         yup = 0;
41083       }
41084       if (ydown>=height()) {
41085         const float d = (float)ydown - height(), D = (float)ydown - yup;
41086         xdown-=(int)(d*((float)xdown - xup)/D);
41087         zdown-=d*(zdown - zup)/D;
41088         txdown-=d*(txdown - txup)/D;
41089         tydown-=d*(tydown - tyup)/D;
41090         ydown = height() - 1;
41091       }
41092       T *ptrd0 = data(nx0,ny0);
41093       int dx = xright - xleft, dy = ydown - yup;
41094       const bool steep = dy>dx;
41095       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
41096       const longT
41097         offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
41098         offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()),
41099         ndx = (longT)(dx>0?dx:1);
41100       const ulongT
41101         whd = (ulongT)_width*_height*_depth,
41102         twh = (ulongT)texture._width*texture._height;
41103 
41104       if (opacity>=1) {
41105         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
41106           if (pattern&hatch) {
41107             const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
41108             const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
41109             T *ptrd = ptrd0;
41110             cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
41111           }
41112           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
41113           ptrd0+=offx;
41114           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
41115         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
41116           const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
41117           const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
41118           T *ptrd = ptrd0;
41119           cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
41120           ptrd0+=offx;
41121           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
41122         }
41123       } else {
41124         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
41125         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
41126           if (pattern&hatch) {
41127             const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
41128             const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
41129             T *ptrd = ptrd0;
41130             cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
41131           }
41132           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
41133           ptrd0+=offx;
41134           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
41135         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
41136           const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
41137           const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
41138           T *ptrd = ptrd0;
41139           cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
41140           ptrd0+=offx;
41141           if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
41142         }
41143       }
41144       return *this;
41145     }
41146 
41147     //! Draw a textured 2d line, with perspective correction and z-buffering.
41148     /**
41149        \param zbuffer Z-buffer image.
41150        \param x0 X-coordinate of the starting point.
41151        \param y0 Y-coordinate of the starting point.
41152        \param z0 Z-coordinate of the starting point
41153        \param x1 X-coordinate of the ending point.
41154        \param y1 Y-coordinate of the ending point.
41155        \param z1 Z-coordinate of the ending point.
41156        \param texture Texture image defining the pixel colors.
41157        \param tx0 X-coordinate of the starting texture point.
41158        \param ty0 Y-coordinate of the starting texture point.
41159        \param tx1 X-coordinate of the ending texture point.
41160        \param ty1 Y-coordinate of the ending texture point.
41161        \param opacity Drawing opacity.
41162        \param pattern An integer whose bits describe the line pattern.
41163        \param init_hatch Tells if the hash variable must be reinitialized.
41164     **/
41165     template<typename tz, typename tc>
41166     CImg<T>& draw_line(CImg<tz>& zbuffer,
41167                        const int x0, const int y0, const float z0,
41168                        const int x1, const int y1, const float z1,
41169                        const CImg<tc>& texture,
41170                        const int tx0, const int ty0,
41171                        const int tx1, const int ty1,
41172                        const float opacity=1,
41173                        const unsigned int pattern=~0U, const bool init_hatch=true) {
41174       typedef typename cimg::superset<tz,float>::type tzfloat;
41175       if (is_empty() || z0<=0 || z1<=0) return *this;
41176       if (!is_sameXY(zbuffer))
41177         throw CImgArgumentException(_cimg_instance
41178                                     "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
41179                                     "different dimensions.",
41180                                     cimg_instance,
41181                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
41182       if (texture._depth>1 || texture._spectrum<_spectrum)
41183         throw CImgArgumentException(_cimg_instance
41184                                     "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
41185                                     cimg_instance,
41186                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
41187       if (is_overlapped(texture))
41188         return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
41189       static unsigned int hatch = ~0U - (~0U>>1);
41190       if (init_hatch) hatch = ~0U - (~0U>>1);
41191       const bool xdir = x0<x1, ydir = y0<y1;
41192       int
41193         nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
41194         &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
41195         &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
41196         &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
41197         &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
41198       float
41199         Tx0 = tx0/z0, Tx1 = tx1/z1,
41200         Ty0 = ty0/z0, Ty1 = ty1/z1,
41201         dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
41202         tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1,
41203         &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
41204         &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
41205         &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
41206         &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
41207       tzfloat
41208         Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1,
41209         dz = Z1 - Z0,  nz0 = Z0, nz1 = Z1,
41210         &zleft = xdir?nz0:nz1,
41211         &zright = xdir?nz1:nz0,
41212         &zup = ydir?nz0:nz1,
41213         &zdown = ydir?nz1:nz0;
41214       if (xright<0 || xleft>=width()) return *this;
41215       if (xleft<0) {
41216         const float D = (float)xright - xleft;
41217         yleft-=(int)((float)xleft*((float)yright - yleft)/D);
41218         zleft-=(float)xleft*(zright - zleft)/D;
41219         txleft-=(float)xleft*(txright - txleft)/D;
41220         tyleft-=(float)xleft*(tyright - tyleft)/D;
41221         xleft = 0;
41222       }
41223       if (xright>=width()) {
41224         const float d = (float)xright - width(), D = (float)xright - xleft;
41225         yright-=(int)(d*((float)yright - yleft)/D);
41226         zright-=d*(zright - zleft)/D;
41227         txright-=d*(txright - txleft)/D;
41228         tyright-=d*(tyright - tyleft)/D;
41229         xright = width() - 1;
41230       }
41231       if (ydown<0 || yup>=height()) return *this;
41232       if (yup<0) {
41233         const float D = (float)ydown - yup;
41234         xup-=(int)((float)yup*((float)xdown - xup)/D);
41235         zup-=yup*(zdown - zup)/D;
41236         txup-=yup*(txdown - txup)/D;
41237         tyup-=yup*(tydown - tyup)/D;
41238         yup = 0;
41239       }
41240       if (ydown>=height()) {
41241         const float d = (float)ydown - height(), D = (float)ydown - yup;
41242         xdown-=(int)(d*((float)xdown - xup)/D);
41243         zdown-=d*(zdown - zup)/D;
41244         txdown-=d*(txdown - txup)/D;
41245         tydown-=d*(tydown - tyup)/D;
41246         ydown = height() - 1;
41247       }
41248       T *ptrd0 = data(nx0,ny0);
41249       tz *ptrz = zbuffer.data(nx0,ny0);
41250       int dx = xright - xleft, dy = ydown - yup;
41251       const bool steep = dy>dx;
41252       if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
41253       const longT
41254         offx = (longT)(nx0<nx1?1:-1)*(steep?width():1),
41255         offy = (longT)(ny0<ny1?1:-1)*(steep?1:width()),
41256         ndx = (longT)(dx>0?dx:1);
41257       const ulongT
41258         whd = (ulongT)_width*_height*_depth,
41259         twh = (ulongT)texture._width*texture._height;
41260 
41261       if (opacity>=1) {
41262         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
41263           if (pattern&hatch) {
41264             const tzfloat z = Z0 + x*dz/ndx;
41265             if (z>=(tzfloat)*ptrz) {
41266               *ptrz = (tz)z;
41267               const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
41268               const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
41269               T *ptrd = ptrd0;
41270               cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
41271             }
41272           }
41273           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
41274           ptrd0+=offx; ptrz+=offx;
41275           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
41276         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
41277           const tzfloat z = Z0 + x*dz/ndx;
41278           if (z>=(tzfloat)*ptrz) {
41279             *ptrz = (tz)z;
41280             const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
41281             const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
41282             T *ptrd = ptrd0;
41283             cimg_forC(*this,c) { *ptrd = (T)*col; ptrd+=whd; col+=twh; }
41284           }
41285           ptrd0+=offx; ptrz+=offx;
41286           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
41287         }
41288       } else {
41289         const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
41290         if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
41291           if (pattern&hatch) {
41292             const tzfloat z = Z0 + x*dz/ndx;
41293             if (z>=(tzfloat)*ptrz) {
41294               *ptrz = (tz)z;
41295               const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
41296               const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
41297               T *ptrd = ptrd0;
41298               cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
41299             }
41300           }
41301           hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
41302           ptrd0+=offx; ptrz+=offx;
41303           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
41304         } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
41305           const tzfloat z = Z0 + x*dz/ndx;
41306           if (z>=(tzfloat)*ptrz) {
41307             *ptrz = (tz)z;
41308             const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
41309             const tc *col = &texture._atXY((int)(tx/z),(int)(ty/z));
41310             T *ptrd = ptrd0;
41311             cimg_forC(*this,c) { *ptrd = (T)(nopacity**col + *ptrd*copacity); ptrd+=whd; col+=twh; }
41312           }
41313           ptrd0+=offx; ptrz+=offx;
41314           if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
41315         }
41316       }
41317       return *this;
41318     }
41319 
41320     //! Draw a set of consecutive lines.
41321     /**
41322        \param points Coordinates of vertices, stored as a list of vectors.
41323        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
41324        \param opacity Drawing opacity.
41325        \param pattern An integer whose bits describe the line pattern.
41326        \param init_hatch If set to true, init hatch motif.
41327        \note
41328        - This function uses several call to the single CImg::draw_line() procedure,
41329        depending on the vectors size in \p points.
41330     **/
41331     template<typename t, typename tc>
41332     CImg<T>& draw_line(const CImg<t>& points,
41333                        const tc *const color, const float opacity=1,
41334                        const unsigned int pattern=~0U, const bool init_hatch=true) {
41335       if (is_empty() || !points || points._width<2) return *this;
41336       bool ninit_hatch = init_hatch;
41337       switch (points._height) {
41338       case 0 : case 1 :
41339         throw CImgArgumentException(_cimg_instance
41340                                     "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).",
41341                                     cimg_instance,
41342                                     points._width,points._height,points._depth,points._spectrum,points._data);
41343 
41344       case 2 : {
41345         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
41346         int ox = x0, oy = y0;
41347         for (unsigned int i = 1; i<points._width; ++i) {
41348           const int x = (int)points(i,0), y = (int)points(i,1);
41349           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
41350           ninit_hatch = false;
41351           ox = x; oy = y;
41352         }
41353       } break;
41354       default : {
41355         const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
41356         int ox = x0, oy = y0, oz = z0;
41357         for (unsigned int i = 1; i<points._width; ++i) {
41358           const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
41359           draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
41360           ninit_hatch = false;
41361           ox = x; oy = y; oz = z;
41362         }
41363       }
41364       }
41365       return *this;
41366     }
41367 
41368     //! Draw a 2d arrow.
41369     /**
41370        \param x0 X-coordinate of the starting arrow point (tail).
41371        \param y0 Y-coordinate of the starting arrow point (tail).
41372        \param x1 X-coordinate of the ending arrow point (head).
41373        \param y1 Y-coordinate of the ending arrow point (head).
41374        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
41375        \param angle Aperture angle of the arrow head.
41376        \param length Length of the arrow head. If negative, describes a percentage of the arrow length.
41377        \param opacity Drawing opacity.
41378        \param pattern An integer whose bits describe the line pattern.
41379     **/
41380     template<typename tc>
41381     CImg<T>& draw_arrow(const int x0, const int y0,
41382                         const int x1, const int y1,
41383                         const tc *const color, const float opacity=1,
41384                         const float angle=30, const float length=-10,
41385                         const unsigned int pattern=~0U) {
41386       if (is_empty()) return *this;
41387       const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
41388         deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f,
41389         l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
41390       if (sq>0) {
41391         const float
41392             cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
41393             cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
41394         const int
41395           xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
41396           xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
41397           xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2;
41398         draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
41399       } else draw_point(x0,y0,color,opacity);
41400       return *this;
41401     }
41402 
41403     //! Draw a 2d spline.
41404     /**
41405        \param x0 X-coordinate of the starting curve point
41406        \param y0 Y-coordinate of the starting curve point
41407        \param u0 X-coordinate of the starting velocity
41408        \param v0 Y-coordinate of the starting velocity
41409        \param x1 X-coordinate of the ending curve point
41410        \param y1 Y-coordinate of the ending curve point
41411        \param u1 X-coordinate of the ending velocity
41412        \param v1 Y-coordinate of the ending velocity
41413        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
41414        \param precision Curve drawing precision.
41415        \param opacity Drawing opacity.
41416        \param pattern An integer whose bits describe the line pattern.
41417        \param init_hatch If \c true, init hatch motif.
41418        \note
41419        - The curve is a 2d cubic Bezier spline, from the set of specified starting/ending points
41420        and corresponding velocity vectors.
41421        - The spline is drawn as a serie of connected segments. The \p precision parameter sets the
41422        average number of pixels in each drawn segment.
41423        - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya),
41424          (\p xb,\p yb), (\p x1,\p y1) } where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point
41425          and (\p xa,\p ya), (\p xb,\p yb) are two
41426        \e control points.
41427        The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from
41428        the control points as
41429        \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).
41430        \par Example:
41431        \code
41432        CImg<unsigned char> img(100,100,1,3,0);
41433        const unsigned char color[] = { 255,255,255 };
41434        img.draw_spline(30,30,0,100,90,40,0,-100,color);
41435        \endcode
41436     **/
41437     template<typename tc>
41438     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
41439                          const int x1, const int y1, const float u1, const float v1,
41440                          const tc *const color, const float opacity=1,
41441                          const float precision=0.25, const unsigned int pattern=~0U,
41442                          const bool init_hatch=true) {
41443       if (is_empty()) return *this;
41444       if (!color)
41445         throw CImgArgumentException(_cimg_instance
41446                                     "draw_spline(): Specified color is (null).",
41447                                     cimg_instance);
41448       if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
41449       bool ninit_hatch = init_hatch;
41450       const float
41451         ax = u0 + u1 + 2*(x0 - x1),
41452         bx = 3*(x1 - x0) - 2*u0 - u1,
41453         ay = v0 + v1 + 2*(y0 - y1),
41454         by = 3*(y1 - y0) - 2*v0 - v1,
41455         _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
41456       int ox = x0, oy = y0;
41457       for (float t = 0; t<1; t+=_precision) {
41458         const float t2 = t*t, t3 = t2*t;
41459         const int
41460           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
41461           ny = (int)(ay*t3 + by*t2 + v0*t + y0);
41462         draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
41463         ninit_hatch = false;
41464         ox = nx; oy = ny;
41465       }
41466       return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
41467     }
41468 
41469     //! Draw a 3d spline \overloading.
41470     /**
41471        \note
41472        - Similar to CImg::draw_spline() for a 3d spline in a volumetric image.
41473     **/
41474     template<typename tc>
41475     CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0,
41476                          const int x1, const int y1, const int z1, const float u1, const float v1, const float w1,
41477                          const tc *const color, const float opacity=1,
41478                          const float precision=4, const unsigned int pattern=~0U,
41479                          const bool init_hatch=true) {
41480       if (is_empty()) return *this;
41481       if (!color)
41482         throw CImgArgumentException(_cimg_instance
41483                                     "draw_spline(): Specified color is (null).",
41484                                     cimg_instance);
41485       if (x0==x1 && y0==y1 && z0==z1) return draw_point(x0,y0,z0,color,opacity);
41486       bool ninit_hatch = init_hatch;
41487       const float
41488         ax = u0 + u1 + 2*(x0 - x1),
41489         bx = 3*(x1 - x0) - 2*u0 - u1,
41490         ay = v0 + v1 + 2*(y0 - y1),
41491         by = 3*(y1 - y0) - 2*v0 - v1,
41492         az = w0 + w1 + 2*(z0 - z1),
41493         bz = 3*(z1 - z0) - 2*w0 - w1,
41494         _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
41495       int ox = x0, oy = y0, oz = z0;
41496       for (float t = 0; t<1; t+=_precision) {
41497         const float t2 = t*t, t3 = t2*t;
41498         const int
41499           nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
41500           ny = (int)(ay*t3 + by*t2 + v0*t + y0),
41501           nz = (int)(az*t3 + bz*t2 + w0*t + z0);
41502         draw_line(ox,oy,oz,nx,ny,nz,color,opacity,pattern,ninit_hatch);
41503         ninit_hatch = false;
41504         ox = nx; oy = ny; oz = nz;
41505       }
41506       return draw_line(ox,oy,oz,x1,y1,z1,color,opacity,pattern,false);
41507     }
41508 
41509     //! Draw a textured 2d spline.
41510     /**
41511        \param x0 X-coordinate of the starting curve point
41512        \param y0 Y-coordinate of the starting curve point
41513        \param u0 X-coordinate of the starting velocity
41514        \param v0 Y-coordinate of the starting velocity
41515        \param x1 X-coordinate of the ending curve point
41516        \param y1 Y-coordinate of the ending curve point
41517        \param u1 X-coordinate of the ending velocity
41518        \param v1 Y-coordinate of the ending velocity
41519        \param texture Texture image defining line pixel colors.
41520        \param tx0 X-coordinate of the starting texture point.
41521        \param ty0 Y-coordinate of the starting texture point.
41522        \param tx1 X-coordinate of the ending texture point.
41523        \param ty1 Y-coordinate of the ending texture point.
41524        \param precision Curve drawing precision.
41525        \param opacity Drawing opacity.
41526        \param pattern An integer whose bits describe the line pattern.
41527        \param init_hatch if \c true, reinit hatch motif.
41528     **/
41529     template<typename t>
41530     CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
41531                          const int x1, const int y1, const float u1, const float v1,
41532                          const CImg<t>& texture,
41533                          const int tx0, const int ty0, const int tx1, const int ty1,
41534                          const float opacity=1,
41535                          const float precision=4, const unsigned int pattern=~0U,
41536                          const bool init_hatch=true) {
41537       if (texture._depth>1 || texture._spectrum<_spectrum)
41538         throw CImgArgumentException(_cimg_instance
41539                                     "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).",
41540                                     cimg_instance,
41541                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
41542       if (is_empty()) return *this;
41543       if (is_overlapped(texture))
41544         return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
41545       if (x0==x1 && y0==y1)
41546         return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
41547                                                       y0<=0?0:y0>=texture.height()?texture.height() - 1:y0),opacity);
41548       bool ninit_hatch = init_hatch;
41549       const float
41550         ax = u0 + u1 + 2*(x0 - x1),
41551         bx = 3*(x1 - x0) - 2*u0 - u1,
41552         ay = v0 + v1 + 2*(y0 - y1),
41553         by = 3*(y1 - y0) - 2*v0 - v1,
41554         _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
41555       int ox = x0, oy = y0, otx = tx0, oty = ty0;
41556       for (float t1 = 0; t1<1; t1+=_precision) {
41557         const float t2 = t1*t1, t3 = t2*t1;
41558         const int
41559           nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
41560           ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
41561           ntx = tx0 + (int)((tx1 - tx0)*t1),
41562           nty = ty0 + (int)((ty1 - ty0)*t1);
41563         draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
41564         ninit_hatch = false;
41565         ox = nx; oy = ny; otx = ntx; oty = nty;
41566       }
41567       return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
41568     }
41569 
41570     //! Draw a set of consecutive splines.
41571     /**
41572        \param points Vertices data.
41573        \param tangents Tangents data.
41574        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
41575        \param opacity Drawing opacity.
41576        \param is_closed_set Tells if the drawn spline set is closed.
41577        \param precision Precision of the drawing.
41578        \param pattern An integer whose bits describe the line pattern.
41579        \param init_hatch If \c true, init hatch motif.
41580     **/
41581     template<typename tp, typename tt, typename tc>
41582     CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
41583                          const tc *const color, const float opacity=1,
41584                          const bool is_closed_set=false, const float precision=4,
41585                          const unsigned int pattern=~0U, const bool init_hatch=true) {
41586       if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
41587       bool ninit_hatch = init_hatch;
41588       switch (points._height) {
41589       case 0 : case 1 :
41590         throw CImgArgumentException(_cimg_instance
41591                                     "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
41592                                     cimg_instance,
41593                                     points._width,points._height,points._depth,points._spectrum,points._data);
41594 
41595       case 2 : {
41596         const int x0 = (int)points(0,0), y0 = (int)points(0,1);
41597         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
41598         int ox = x0, oy = y0;
41599         float ou = u0, ov = v0;
41600         for (unsigned int i = 1; i<points._width; ++i) {
41601           const int x = (int)points(i,0), y = (int)points(i,1);
41602           const float u = (float)tangents(i,0), v = (float)tangents(i,1);
41603           draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
41604           ninit_hatch = false;
41605           ox = x; oy = y; ou = u; ov = v;
41606         }
41607         if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
41608       } break;
41609       default : {
41610         const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
41611         const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1), w0 = (float)tangents(0,2);
41612         int ox = x0, oy = y0, oz = z0;
41613         float ou = u0, ov = v0, ow = w0;
41614         for (unsigned int i = 1; i<points._width; ++i) {
41615           const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
41616           const float u = (float)tangents(i,0), v = (float)tangents(i,1), w = (float)tangents(i,2);
41617           draw_spline(ox,oy,oz,ou,ov,ow,x,y,z,u,v,w,color,opacity,pattern,ninit_hatch);
41618           ninit_hatch = false;
41619           ox = x; oy = y; oz = z; ou = u; ov = v; ow = w;
41620         }
41621         if (is_closed_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false);
41622       }
41623       }
41624       return *this;
41625     }
41626 
41627     //! Draw a set of consecutive splines \overloading.
41628     /**
41629        Similar to previous function, with the point tangents automatically estimated from the given points set.
41630     **/
41631     template<typename tp, typename tc>
41632     CImg<T>& draw_spline(const CImg<tp>& points,
41633                          const tc *const color, const float opacity=1,
41634                          const bool is_closed_set=false, const float precision=4,
41635                          const unsigned int pattern=~0U, const bool init_hatch=true) {
41636       if (is_empty() || !points || points._width<2) return *this;
41637       CImg<Tfloat> tangents;
41638       switch (points._height) {
41639       case 0 : case 1 :
41640         throw CImgArgumentException(_cimg_instance
41641                                     "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
41642                                     cimg_instance,
41643                                     points._width,points._height,points._depth,points._spectrum,points._data);
41644       case 2 : {
41645         tangents.assign(points._width,points._height);
41646         cimg_forX(points,p) {
41647           const unsigned int
41648             p0 = is_closed_set?(p + points._width - 1)%points._width:(p?p - 1:0),
41649             p1 = is_closed_set?(p + 1)%points._width:(p + 1<points._width?p + 1:p);
41650           const float
41651             x = (float)points(p,0),
41652             y = (float)points(p,1),
41653             x0 = (float)points(p0,0),
41654             y0 = (float)points(p0,1),
41655             x1 = (float)points(p1,0),
41656             y1 = (float)points(p1,1),
41657             u0 = x - x0,
41658             v0 = y - y0,
41659             n0 = 1e-8f + cimg::hypot(u0,v0),
41660             u1 = x1 - x,
41661             v1 = y1 - y,
41662             n1 = 1e-8f + cimg::hypot(u1,v1),
41663             u = u0/n0 + u1/n1,
41664             v = v0/n0 + v1/n1,
41665             n = 1e-8f + cimg::hypot(u,v),
41666             fact = 0.5f*(n0 + n1);
41667           tangents(p,0) = (Tfloat)(fact*u/n);
41668           tangents(p,1) = (Tfloat)(fact*v/n);
41669         }
41670       } break;
41671       default : {
41672         tangents.assign(points._width,points._height);
41673         cimg_forX(points,p) {
41674           const unsigned int
41675             p0 = is_closed_set?(p + points._width - 1)%points._width:(p?p - 1:0),
41676             p1 = is_closed_set?(p + 1)%points._width:(p + 1<points._width?p + 1:p);
41677           const float
41678             x = (float)points(p,0),
41679             y = (float)points(p,1),
41680             z = (float)points(p,2),
41681             x0 = (float)points(p0,0),
41682             y0 = (float)points(p0,1),
41683             z0 = (float)points(p0,2),
41684             x1 = (float)points(p1,0),
41685             y1 = (float)points(p1,1),
41686             z1 = (float)points(p1,2),
41687             u0 = x - x0,
41688             v0 = y - y0,
41689             w0 = z - z0,
41690             n0 = 1e-8f + cimg::hypot(u0,v0,w0),
41691             u1 = x1 - x,
41692             v1 = y1 - y,
41693             w1 = z1 - z,
41694             n1 = 1e-8f + cimg::hypot(u1,v1,w1),
41695             u = u0/n0 + u1/n1,
41696             v = v0/n0 + v1/n1,
41697             w = w0/n0 + w1/n1,
41698             n = 1e-8f + cimg::hypot(u,v,w),
41699             fact = 0.5f*(n0 + n1);
41700           tangents(p,0) = (Tfloat)(fact*u/n);
41701           tangents(p,1) = (Tfloat)(fact*v/n);
41702           tangents(p,2) = (Tfloat)(fact*w/n);
41703         }
41704       }
41705       }
41706       return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
41707     }
41708 
41709     // Inner macro for drawing triangles.
41710 #define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \
41711         for (int y = y0<0?0:y0, \
41712                xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
41713                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
41714                _sxn=1, \
41715                _sxr=1, \
41716                _sxl=1, \
41717                _dxn = x2>x1?x2-x1:(_sxn=-1,x1 - x2), \
41718                _dxr = x2>x0?x2-x0:(_sxr=-1,x0 - x2), \
41719                _dxl = x1>x0?x1-x0:(_sxl=-1,x0 - x1), \
41720                _dyn = y2-y1, \
41721                _dyr = y2-y0, \
41722                _dyl = y1-y0, \
41723                _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
41724                            _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
41725                            _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
41726                            std::min((int)(img)._height - y - 1,y2 - y)), \
41727                _errn = _dyn/2, \
41728                _errr = _dyr/2, \
41729                _errl = _dyl/2, \
41730                _rxn = _dyn?(x2-x1)/_dyn:0, \
41731                _rxr = _dyr?(x2-x0)/_dyr:0, \
41732                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
41733                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \
41734              _counter>=0; --_counter, ++y, \
41735                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
41736                xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \
41737                            (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
41738 
41739 #define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \
41740         for (int y = y0<0?0:y0, \
41741                xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
41742                cr = y0>=0?c0:(c0 - y0*(c2 - c0)/(y2 - y0)), \
41743                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
41744                cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0 - y0*(c1 - c0)/(y1 - y0))):(c1 - y1*(c2 - c1)/(y2 - y1)), \
41745                _sxn=1, _scn=1, \
41746                _sxr=1, _scr=1, \
41747                _sxl=1, _scl=1, \
41748                _dxn = x2>x1?x2-x1:(_sxn=-1,x1 - x2), \
41749                _dxr = x2>x0?x2-x0:(_sxr=-1,x0 - x2), \
41750                _dxl = x1>x0?x1-x0:(_sxl=-1,x0 - x1), \
41751                _dcn = c2>c1?c2-c1:(_scn=-1,c1 - c2), \
41752                _dcr = c2>c0?c2-c0:(_scr=-1,c0 - c2), \
41753                _dcl = c1>c0?c1-c0:(_scl=-1,c0 - c1), \
41754                _dyn = y2-y1, \
41755                _dyr = y2-y0, \
41756                _dyl = y1-y0, \
41757                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
41758                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
41759                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
41760                           _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
41761                           _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
41762                           _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
41763                           std::min((int)(img)._height - y - 1,y2 - y)), \
41764                _errn = _dyn/2, _errcn = _errn, \
41765                _errr = _dyr/2, _errcr = _errr, \
41766                _errl = _dyl/2, _errcl = _errl, \
41767                _rxn = _dyn?(x2 - x1)/_dyn:0, \
41768                _rcn = _dyn?(c2 - c1)/_dyn:0, \
41769                _rxr = _dyr?(x2 - x0)/_dyr:0, \
41770                _rcr = _dyr?(c2 - c0)/_dyr:0, \
41771                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
41772                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
41773                _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
41774                                        (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \
41775              _counter>=0; --_counter, ++y, \
41776                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
41777                cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
41778                xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
41779       	                   _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
41780                (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
41781                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
41782 
41783 #define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \
41784         for (int y = y0<0?0:y0, \
41785                xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
41786                txr = y0>=0?tx0:(tx0 - y0*(tx2 - tx0)/(y2 - y0)), \
41787                tyr = y0>=0?ty0:(ty0 - y0*(ty2 - ty0)/(y2 - y0)), \
41788                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
41789                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0 - y0*(tx1 - tx0)/(y1 - y0))):(tx1 - y1*(tx2 - tx1)/(y2 - y1)), \
41790                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0 - y0*(ty1 - ty0)/(y1 - y0))):(ty1 - y1*(ty2 - ty1)/(y2 - y1)), \
41791                _sxn=1, _stxn=1, _styn=1, \
41792                _sxr=1, _stxr=1, _styr=1, \
41793                _sxl=1, _stxl=1, _styl=1, \
41794                _dxn = x2>x1?x2 - x1:(_sxn=-1,x1 - x2), \
41795                _dxr = x2>x0?x2 - x0:(_sxr=-1,x0 - x2), \
41796                _dxl = x1>x0?x1 - x0:(_sxl=-1,x0 - x1), \
41797                _dtxn = tx2>tx1?tx2 - tx1:(_stxn=-1,tx1 - tx2), \
41798                _dtxr = tx2>tx0?tx2 - tx0:(_stxr=-1,tx0 - tx2), \
41799                _dtxl = tx1>tx0?tx1 - tx0:(_stxl=-1,tx0 - tx1), \
41800                _dtyn = ty2>ty1?ty2 - ty1:(_styn=-1,ty1 - ty2), \
41801                _dtyr = ty2>ty0?ty2 - ty0:(_styr=-1,ty0 - ty2), \
41802                _dtyl = ty1>ty0?ty1 - ty0:(_styl=-1,ty0 - ty1), \
41803                _dyn = y2-y1, \
41804                _dyr = y2-y0, \
41805                _dyl = y1-y0, \
41806                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
41807                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
41808                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
41809                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
41810                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
41811                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
41812                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
41813                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
41814                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
41815                           std::min((int)(img)._height - y - 1,y2 - y)), \
41816                _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \
41817                _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \
41818                _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \
41819                _rxn = _dyn?(x2 - x1)/_dyn:0, \
41820                _rtxn = _dyn?(tx2 - tx1)/_dyn:0, \
41821                _rtyn = _dyn?(ty2 - ty1)/_dyn:0, \
41822                _rxr = _dyr?(x2 - x0)/_dyr:0, \
41823                _rtxr = _dyr?(tx2 - tx0)/_dyr:0, \
41824                _rtyr = _dyr?(ty2 - ty0)/_dyr:0, \
41825                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1 - x0)/_dyl:0): \
41826                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
41827                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1 - tx0)/_dyl:0): \
41828                                        (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
41829                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1 - ty0)/_dyl:0): \
41830                                        (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
41831              _counter>=0; --_counter, ++y, \
41832                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
41833                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
41834                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
41835                xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
41836                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
41837                            _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
41838                (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
41839                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\
41840                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl))
41841 
41842 #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) \
41843         for (int y = y0<0?0:y0, \
41844                xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
41845                cr = y0>=0?c0:(c0 - y0*(c2 - c0)/(y2 - y0)), \
41846                txr = y0>=0?tx0:(tx0 - y0*(tx2 - tx0)/(y2 - y0)), \
41847                tyr = y0>=0?ty0:(ty0 - y0*(ty2 - ty0)/(y2 - y0)), \
41848                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
41849                cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0 - y0*(c1 - c0)/(y1 - y0))):(c1 - y1*(c2 - c1)/(y2 - y1)), \
41850                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0 - y0*(tx1 - tx0)/(y1 - y0))):(tx1 - y1*(tx2 - tx1)/(y2 - y1)), \
41851                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0 - y0*(ty1 - ty0)/(y1 - y0))):(ty1 - y1*(ty2 - ty1)/(y2 - y1)), \
41852                _sxn=1, _scn=1, _stxn=1, _styn=1, \
41853                _sxr=1, _scr=1, _stxr=1, _styr=1, \
41854                _sxl=1, _scl=1, _stxl=1, _styl=1, \
41855                _dxn = x2>x1?x2 - x1:(_sxn=-1,x1 - x2), \
41856                _dxr = x2>x0?x2 - x0:(_sxr=-1,x0 - x2), \
41857                _dxl = x1>x0?x1 - x0:(_sxl=-1,x0 - x1), \
41858                _dcn = c2>c1?c2 - c1:(_scn=-1,c1 - c2), \
41859                _dcr = c2>c0?c2 - c0:(_scr=-1,c0 - c2), \
41860                _dcl = c1>c0?c1 - c0:(_scl=-1,c0 - c1), \
41861                _dtxn = tx2>tx1?tx2 - tx1:(_stxn=-1,tx1 - tx2), \
41862                _dtxr = tx2>tx0?tx2 - tx0:(_stxr=-1,tx0 - tx2), \
41863                _dtxl = tx1>tx0?tx1 - tx0:(_stxl=-1,tx0 - tx1), \
41864                _dtyn = ty2>ty1?ty2 - ty1:(_styn=-1,ty1 - ty2), \
41865                _dtyr = ty2>ty0?ty2 - ty0:(_styr=-1,ty0 - ty2), \
41866                _dtyl = ty1>ty0?ty1 - ty0:(_styl=-1,ty0 - ty1), \
41867                _dyn = y2 - y1, \
41868                _dyr = y2 - y0, \
41869                _dyl = y1 - y0, \
41870                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
41871                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
41872                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
41873                           _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
41874                           _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
41875                           _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
41876                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
41877                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
41878                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
41879                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
41880                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
41881                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
41882                           std::min((int)(img)._height - y - 1,y2 - y)), \
41883                _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \
41884                _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \
41885                _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \
41886                _rxn = _dyn?(x2 - x1)/_dyn:0, \
41887                _rcn = _dyn?(c2 - c1)/_dyn:0, \
41888                _rtxn = _dyn?(tx2 - tx1)/_dyn:0, \
41889                _rtyn = _dyn?(ty2 - ty1)/_dyn:0, \
41890                _rxr = _dyr?(x2 - x0)/_dyr:0, \
41891                _rcr = _dyr?(c2 - c0)/_dyr:0, \
41892                _rtxr = _dyr?(tx2 - tx0)/_dyr:0, \
41893                _rtyr = _dyr?(ty2 - ty0)/_dyr:0, \
41894                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1 - x0)/_dyl:0): \
41895                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
41896                _rcl = (y0!=y1 && y1>0)?(_dyl?(c1 - c0)/_dyl:0): \
41897                                        (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \
41898                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1 - tx0)/_dyl:0): \
41899                                         (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
41900                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1 - ty0)/_dyl:0): \
41901                                         (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
41902              _counter>=0; --_counter, ++y, \
41903                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
41904                cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
41905                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
41906                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
41907                xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
41908                             txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
41909                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
41910                             _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
41911                (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
41912                 _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
41913                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
41914                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl))
41915 
41916 #define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,\
41917                             tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \
41918         for (int y = y0<0?0:y0, \
41919                xr = y0>=0?x0:(x0 - y0*(x2 - x0)/(y2 - y0)), \
41920                txr = y0>=0?tx0:(tx0 - y0*(tx2 - tx0)/(y2 - y0)), \
41921                tyr = y0>=0?ty0:(ty0 - y0*(ty2 - ty0)/(y2 - y0)), \
41922                lxr = y0>=0?lx0:(lx0 - y0*(lx2 - lx0)/(y2 - y0)), \
41923                lyr = y0>=0?ly0:(ly0 - y0*(ly2 - ly0)/(y2 - y0)), \
41924                xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0 - y0*(x1 - x0)/(y1 - y0))):(x1 - y1*(x2 - x1)/(y2 - y1)), \
41925                txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0 - y0*(tx1 - tx0)/(y1 - y0))):(tx1 - y1*(tx2 - tx1)/(y2 - y1)), \
41926                tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0 - y0*(ty1 - ty0)/(y1 - y0))):(ty1 - y1*(ty2 - ty1)/(y2 - y1)), \
41927                lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0 - y0*(lx1 - lx0)/(y1 - y0))):(lx1 - y1*(lx2 - lx1)/(y2 - y1)), \
41928                lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0 - y0*(ly1 - ly0)/(y1 - y0))):(ly1 - y1*(ly2 - ly1)/(y2 - y1)), \
41929                _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \
41930                _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \
41931                _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \
41932                _dxn = x2>x1?x2 - x1:(_sxn=-1,x1 - x2), _dyn = y2 - y1, \
41933                _dxr = x2>x0?x2 - x0:(_sxr=-1,x0 - x2), _dyr = y2 - y0, \
41934                _dxl = x1>x0?x1 - x0:(_sxl=-1,x0 - x1), _dyl = y1 - y0, \
41935                _dtxn = tx2>tx1?tx2 - tx1:(_stxn=-1,tx1 - tx2), \
41936                _dtxr = tx2>tx0?tx2 - tx0:(_stxr=-1,tx0 - tx2), \
41937                _dtxl = tx1>tx0?tx1 - tx0:(_stxl=-1,tx0 - tx1), \
41938                _dtyn = ty2>ty1?ty2 - ty1:(_styn=-1,ty1 - ty2), \
41939                _dtyr = ty2>ty0?ty2 - ty0:(_styr=-1,ty0 - ty2), \
41940                _dtyl = ty1>ty0?ty1 - ty0:(_styl=-1,ty0 - ty1), \
41941                _dlxn = lx2>lx1?lx2 - lx1:(_slxn=-1,lx1 - lx2), \
41942                _dlxr = lx2>lx0?lx2 - lx0:(_slxr=-1,lx0 - lx2), \
41943                _dlxl = lx1>lx0?lx1 - lx0:(_slxl=-1,lx0 - lx1), \
41944                _dlyn = ly2>ly1?ly2 - ly1:(_slyn=-1,ly1 - ly2), \
41945                _dlyr = ly2>ly0?ly2 - ly0:(_slyr=-1,ly0 - ly2), \
41946                _dlyl = ly1>ly0?ly1 - ly0:(_slyl=-1,ly0 - ly1), \
41947                _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
41948                           _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
41949                           _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
41950                           _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
41951                           _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
41952                           _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
41953                           _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
41954                           _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
41955                           _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
41956                           _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \
41957                           _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \
41958                           _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \
41959                           _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \
41960                           _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \
41961                           _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \
41962                           std::min((int)(img)._height - y - 1,y2 - y)), \
41963                _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \
41964                _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \
41965                _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \
41966                _rxn = _dyn?(x2 - x1)/_dyn:0, \
41967                _rtxn = _dyn?(tx2 - tx1)/_dyn:0, \
41968                _rtyn = _dyn?(ty2 - ty1)/_dyn:0, \
41969                _rlxn = _dyn?(lx2 - lx1)/_dyn:0, \
41970                _rlyn = _dyn?(ly2 - ly1)/_dyn:0, \
41971                _rxr = _dyr?(x2 - x0)/_dyr:0, \
41972                _rtxr = _dyr?(tx2 - tx0)/_dyr:0, \
41973                _rtyr = _dyr?(ty2 - ty0)/_dyr:0, \
41974                _rlxr = _dyr?(lx2 - lx0)/_dyr:0, \
41975                _rlyr = _dyr?(ly2 - ly0)/_dyr:0, \
41976                _rxl = (y0!=y1 && y1>0)?(_dyl?(x1 - x0)/_dyl:0): \
41977                                        (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
41978                _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1 - tx0)/_dyl:0): \
41979                                         (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
41980                _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1 - ty0)/_dyl:0): \
41981                                         (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \
41982                _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1 - lx0)/_dyl:0): \
41983                                         (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \
41984                _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1 - ly0)/_dyl:0): \
41985                                         (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \
41986              _counter>=0; --_counter, ++y, \
41987                xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
41988                txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
41989                tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
41990                lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \
41991                lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \
41992                xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
41993                             tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
41994                             lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \
41995                             lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \
41996                             _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
41997                (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
41998                 _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
41999                 _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \
42000                 _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \
42001                 _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1 - xl))
42002 
42003     // [internal] Draw a filled triangle.
42004     template<typename tc>
42005     CImg<T>& _draw_triangle(const int x0, const int y0,
42006                             const int x1, const int y1,
42007                             const int x2, const int y2,
42008                             const tc *const color, const float opacity,
42009                             const float brightness) {
42010       cimg_init_scanline(color,opacity);
42011       const float nbrightness = cimg::cut(brightness,0,2);
42012       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
42013       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1);
42014       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2);
42015       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2);
42016       if (ny0<height() && ny2>=0) {
42017         if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0)
42018           _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2)
42019             cimg_draw_scanline(xl,xr,y,color,opacity,nbrightness);
42020         else
42021           _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2)
42022             cimg_draw_scanline(xr,xl,y,color,opacity,nbrightness);
42023       }
42024       return *this;
42025     }
42026 
42027     //! Draw a filled 2d triangle.
42028     /**
42029        \param x0 X-coordinate of the first vertex.
42030        \param y0 Y-coordinate of the first vertex.
42031        \param x1 X-coordinate of the second vertex.
42032        \param y1 Y-coordinate of the second vertex.
42033        \param x2 X-coordinate of the third vertex.
42034        \param y2 Y-coordinate of the third vertex.
42035        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
42036        \param opacity Drawing opacity.
42037      **/
42038     template<typename tc>
42039     CImg<T>& draw_triangle(const int x0, const int y0,
42040                            const int x1, const int y1,
42041                            const int x2, const int y2,
42042                            const tc *const color, const float opacity=1) {
42043       if (is_empty()) return *this;
42044       if (!color)
42045         throw CImgArgumentException(_cimg_instance
42046                                     "draw_triangle(): Specified color is (null).",
42047                                     cimg_instance);
42048       _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
42049       return *this;
42050     }
42051 
42052     //! Draw a outlined 2d triangle.
42053     /**
42054        \param x0 X-coordinate of the first vertex.
42055        \param y0 Y-coordinate of the first vertex.
42056        \param x1 X-coordinate of the second vertex.
42057        \param y1 Y-coordinate of the second vertex.
42058        \param x2 X-coordinate of the third vertex.
42059        \param y2 Y-coordinate of the third vertex.
42060        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
42061        \param opacity Drawing opacity.
42062        \param pattern An integer whose bits describe the outline pattern.
42063      **/
42064     template<typename tc>
42065     CImg<T>& draw_triangle(const int x0, const int y0,
42066                            const int x1, const int y1,
42067                            const int x2, const int y2,
42068                            const tc *const color, const float opacity,
42069                            const unsigned int pattern) {
42070       if (is_empty()) return *this;
42071       if (!color)
42072         throw CImgArgumentException(_cimg_instance
42073                                     "draw_triangle(): Specified color is (null).",
42074                                     cimg_instance);
42075       draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
42076         draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
42077         draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
42078       return *this;
42079     }
42080 
42081     //! Draw a filled 2d triangle, with z-buffering.
42082     /**
42083        \param zbuffer Z-buffer image.
42084        \param x0 X-coordinate of the first vertex.
42085        \param y0 Y-coordinate of the first vertex.
42086        \param z0 Z-coordinate of the first vertex.
42087        \param x1 X-coordinate of the second vertex.
42088        \param y1 Y-coordinate of the second vertex.
42089        \param z1 Z-coordinate of the second vertex.
42090        \param x2 X-coordinate of the third vertex.
42091        \param y2 Y-coordinate of the third vertex.
42092        \param z2 Z-coordinate of the third vertex.
42093        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
42094        \param opacity Drawing opacity.
42095        \param brightness Brightness factor.
42096     **/
42097     template<typename tz, typename tc>
42098     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
42099                            const int x0, const int y0, const float z0,
42100                            const int x1, const int y1, const float z1,
42101                            const int x2, const int y2, const float z2,
42102                            const tc *const color, const float opacity=1,
42103                            const float brightness=1) {
42104       typedef typename cimg::superset<tz,float>::type tzfloat;
42105       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
42106       if (!color)
42107         throw CImgArgumentException(_cimg_instance
42108                                     "draw_triangle(): Specified color is (null).",
42109                                     cimg_instance);
42110       if (!is_sameXY(zbuffer))
42111         throw CImgArgumentException(_cimg_instance
42112                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
42113                                     "different dimensions.",
42114                                     cimg_instance,
42115                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
42116       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
42117       const float
42118         nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f),
42119         nbrightness = cimg::cut(brightness,0,2);
42120       const longT whd = (longT)width()*height()*depth(), offx = spectrum()*whd;
42121       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
42122       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
42123       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
42124       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2);
42125       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2);
42126       if (ny0>=height() || ny2<0) return *this;
42127       tzfloat
42128         pzl = (nz1 - nz0)/(ny1 - ny0),
42129         pzr = (nz2 - nz0)/(ny2 - ny0),
42130         pzn = (nz2 - nz1)/(ny2 - ny1),
42131         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
42132         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
42133       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
42134         if (y==ny1) { zl = nz1; pzl = pzn; }
42135         int xleft = xleft0, xright = xright0;
42136         tzfloat zleft = zl, zright = zr;
42137         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright);
42138         const int dx = xright - xleft;
42139         const tzfloat pentez = (zright - zleft)/dx;
42140         if (xleft<0 && dx) zleft-=xleft*(zright - zleft)/dx;
42141         if (xleft<0) xleft = 0;
42142         if (xright>=width() - 1) xright = width() - 1;
42143         T* ptrd = data(xleft,y,0,0);
42144         tz *ptrz = xleft<=xright?zbuffer.data(xleft,y):0;
42145         if (opacity>=1) {
42146           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42147               if (zleft>=(tzfloat)*ptrz) {
42148                 *ptrz = (tz)zleft;
42149               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
42150               ptrd-=offx;
42151             }
42152             zleft+=pentez;
42153           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42154               if (zleft>=(tzfloat)*ptrz) {
42155                 *ptrz = (tz)zleft;
42156               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whd; }
42157               ptrd-=offx;
42158             }
42159             zleft+=pentez;
42160           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42161               if (zleft>=(tzfloat)*ptrz) {
42162                 *ptrz = (tz)zleft;
42163               const tc *col = color;
42164               cimg_forC(*this,c) { *ptrd = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval); ptrd+=whd; }
42165               ptrd-=offx;
42166             }
42167             zleft+=pentez;
42168           }
42169         } else {
42170           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42171               if (zleft>=(tzfloat)*ptrz) {
42172                 *ptrz = (tz)zleft;
42173               const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whd; }
42174               ptrd-=offx;
42175             }
42176             zleft+=pentez;
42177           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42178               if (zleft>=(tzfloat)*ptrz) {
42179                 *ptrz = (tz)zleft;
42180               const tc *col = color;
42181               cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; }
42182               ptrd-=offx;
42183             }
42184             zleft+=pentez;
42185           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42186               if (zleft>=(tzfloat)*ptrz) {
42187                 *ptrz = (tz)zleft;
42188               const tc *col = color;
42189               cimg_forC(*this,c) {
42190                 const T val = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval);
42191                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
42192                 ptrd+=whd;
42193               }
42194               ptrd-=offx;
42195             }
42196             zleft+=pentez;
42197           }
42198         }
42199         zr+=pzr; zl+=pzl;
42200       }
42201       return *this;
42202     }
42203 
42204     //! Draw a Gouraud-shaded 2d triangle.
42205     /**
42206        \param x0 X-coordinate of the first vertex in the image instance.
42207        \param y0 Y-coordinate of the first vertex in the image instance.
42208        \param x1 X-coordinate of the second vertex in the image instance.
42209        \param y1 Y-coordinate of the second vertex in the image instance.
42210        \param x2 X-coordinate of the third vertex in the image instance.
42211        \param y2 Y-coordinate of the third vertex in the image instance.
42212        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
42213        \param brightness0 Brightness factor of the first vertex (in [0,2]).
42214        \param brightness1 brightness factor of the second vertex (in [0,2]).
42215        \param brightness2 brightness factor of the third vertex (in [0,2]).
42216        \param opacity Drawing opacity.
42217     **/
42218     template<typename tc>
42219     CImg<T>& draw_triangle(const int x0, const int y0,
42220                            const int x1, const int y1,
42221                            const int x2, const int y2,
42222                            const tc *const color,
42223                            const float brightness0,
42224                            const float brightness1,
42225                            const float brightness2,
42226                            const float opacity=1) {
42227       if (is_empty()) return *this;
42228       if (!color)
42229         throw CImgArgumentException(_cimg_instance
42230                                     "draw_triangle(): Specified color is (null).",
42231                                     cimg_instance);
42232       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
42233       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
42234       const longT whd = (longT)width()*height()*depth(), offx = spectrum()*whd - 1;
42235       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
42236         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
42237         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
42238         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
42239       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1);
42240       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2);
42241       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2);
42242       if (ny0>=height() || ny2<0) return *this;
42243       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
42244         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
42245         if (xright<xleft) cimg::swap(xleft,xright,cleft,cright);
42246         const int
42247           dx = xright - xleft,
42248           dc = cright>cleft?cright - cleft:cleft - cright,
42249           rc = dx?(cright - cleft)/dx:0,
42250           sc = cright>cleft?1:-1,
42251           ndc = dc - (dx?dx*(dc/dx):0);
42252         int errc = dx>>1;
42253         if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx;
42254         if (xleft<0) xleft = 0;
42255         if (xright>=width() - 1) xright = width() - 1;
42256         T* ptrd = data(xleft,y);
42257         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
42258           const tc *col = color;
42259           cimg_forC(*this,c) {
42260             *ptrd = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256);
42261             ptrd+=whd;
42262           }
42263           ptrd-=offx;
42264           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
42265         } else for (int x = xleft; x<=xright; ++x) {
42266           const tc *col = color;
42267           cimg_forC(*this,c) {
42268             const T val = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256);
42269             *ptrd = (T)(nopacity*val + *ptrd*copacity);
42270             ptrd+=whd;
42271           }
42272           ptrd-=offx;
42273           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
42274         }
42275       }
42276       return *this;
42277     }
42278 
42279     //! Draw a Gouraud-shaded 2d triangle, with z-buffering \overloading.
42280     template<typename tz, typename tc>
42281     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
42282                            const int x0, const int y0, const float z0,
42283                            const int x1, const int y1, const float z1,
42284                            const int x2, const int y2, const float z2,
42285                            const tc *const color,
42286                            const float brightness0,
42287                            const float brightness1,
42288                            const float brightness2,
42289                            const float opacity=1) {
42290       typedef typename cimg::superset<tz,float>::type tzfloat;
42291       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
42292       if (!color)
42293         throw CImgArgumentException(_cimg_instance
42294                                     "draw_triangle(): Specified color is (null).",
42295                                     cimg_instance);
42296       if (!is_sameXY(zbuffer))
42297         throw CImgArgumentException(_cimg_instance
42298                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
42299                                     "different dimensions.",
42300                                     cimg_instance,
42301                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
42302       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
42303       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
42304       const longT whd = (longT)width()*height()*depth(), offx = spectrum()*whd;
42305       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
42306         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
42307         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
42308         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
42309       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
42310       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1);
42311       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2);
42312       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2);
42313       if (ny0>=height() || ny2<0) return *this;
42314       tzfloat
42315         pzl = (nz1 - nz0)/(ny1 - ny0),
42316         pzr = (nz2 - nz0)/(ny2 - ny0),
42317         pzn = (nz2 - nz1)/(ny2 - ny1),
42318         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
42319         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
42320       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
42321         if (y==ny1) { zl = nz1; pzl = pzn; }
42322         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
42323         tzfloat zleft = zl, zright = zr;
42324         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,cleft,cright);
42325         const int
42326           dx = xright - xleft,
42327           dc = cright>cleft?cright - cleft:cleft - cright,
42328           rc = dx?(cright - cleft)/dx:0,
42329           sc = cright>cleft?1:-1,
42330           ndc = dc - (dx?dx*(dc/dx):0);
42331         const tzfloat pentez = (zright - zleft)/dx;
42332         int errc = dx>>1;
42333         if (xleft<0 && dx) {
42334           cleft-=xleft*(cright - cleft)/dx;
42335           zleft-=xleft*(zright - zleft)/dx;
42336         }
42337         if (xleft<0) xleft = 0;
42338         if (xright>=width() - 1) xright = width() - 1;
42339         T *ptrd = data(xleft,y);
42340         tz *ptrz = xleft<=xright?zbuffer.data(xleft,y):0;
42341         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
42342             if (zleft>=(tzfloat)*ptrz) {
42343               *ptrz = (tz)zleft;
42344               const tc *col = color;
42345               cimg_forC(*this,c) {
42346                 *ptrd = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256);
42347                 ptrd+=whd;
42348               }
42349               ptrd-=offx;
42350             }
42351             zleft+=pentez;
42352             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
42353           } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
42354             if (zleft>=(tzfloat)*ptrz) {
42355               *ptrz = (tz)zleft;
42356               const tc *col = color;
42357               cimg_forC(*this,c) {
42358                 const T val = (T)(cleft<256?cleft**(col++)/256:((512 - cleft)**(col++)+(cleft - 256)*maxval)/256);
42359                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
42360                 ptrd+=whd;
42361               }
42362               ptrd-=offx;
42363             }
42364             zleft+=pentez;
42365             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
42366           }
42367         zr+=pzr; zl+=pzl;
42368       }
42369       return *this;
42370     }
42371 
42372     //! Draw a color-interpolated 2d triangle.
42373     /**
42374        \param x0 X-coordinate of the first vertex in the image instance.
42375        \param y0 Y-coordinate of the first vertex in the image instance.
42376        \param x1 X-coordinate of the second vertex in the image instance.
42377        \param y1 Y-coordinate of the second vertex in the image instance.
42378        \param x2 X-coordinate of the third vertex in the image instance.
42379        \param y2 Y-coordinate of the third vertex in the image instance.
42380        \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex.
42381        \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the seconf vertex.
42382        \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex.
42383        \param opacity Drawing opacity.
42384      **/
42385     template<typename tc1, typename tc2, typename tc3>
42386     CImg<T>& draw_triangle(const int x0, const int y0,
42387                            const int x1, const int y1,
42388                            const int x2, const int y2,
42389                            const tc1 *const color1,
42390                            const tc2 *const color2,
42391                            const tc3 *const color3,
42392                            const float opacity=1) {
42393       const unsigned char one = 1;
42394       cimg_forC(*this,c)
42395         get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity);
42396       return *this;
42397     }
42398 
42399     //! Draw a textured 2d triangle.
42400     /**
42401        \param x0 X-coordinate of the first vertex in the image instance.
42402        \param y0 Y-coordinate of the first vertex in the image instance.
42403        \param x1 X-coordinate of the second vertex in the image instance.
42404        \param y1 Y-coordinate of the second vertex in the image instance.
42405        \param x2 X-coordinate of the third vertex in the image instance.
42406        \param y2 Y-coordinate of the third vertex in the image instance.
42407        \param texture Texture image used to fill the triangle.
42408        \param tx0 X-coordinate of the first vertex in the texture image.
42409        \param ty0 Y-coordinate of the first vertex in the texture image.
42410        \param tx1 X-coordinate of the second vertex in the texture image.
42411        \param ty1 Y-coordinate of the second vertex in the texture image.
42412        \param tx2 X-coordinate of the third vertex in the texture image.
42413        \param ty2 Y-coordinate of the third vertex in the texture image.
42414        \param opacity Drawing opacity.
42415        \param brightness Brightness factor of the drawing (in [0,2]).
42416     **/
42417     template<typename tc>
42418     CImg<T>& draw_triangle(const int x0, const int y0,
42419                            const int x1, const int y1,
42420                            const int x2, const int y2,
42421                            const CImg<tc>& texture,
42422                            const int tx0, const int ty0,
42423                            const int tx1, const int ty1,
42424                            const int tx2, const int ty2,
42425                            const float opacity=1,
42426                            const float brightness=1) {
42427       if (is_empty()) return *this;
42428       if (texture._depth>1 || texture._spectrum<_spectrum)
42429         throw CImgArgumentException(_cimg_instance
42430                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
42431                                     cimg_instance,
42432                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
42433       if (is_overlapped(texture))
42434         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
42435       static const T maxval = (T)std::min(cimg::type<T>::max(),cimg::type<tc>::max());
42436       const float
42437         nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f),
42438         nbrightness = cimg::cut(brightness,0,2);
42439       const ulongT
42440         whd = (ulongT)_width*_height*_depth,
42441         twh = (ulongT)texture._width*texture._height,
42442         offx = _spectrum*whd - 1;
42443       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
42444         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2;
42445       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1);
42446       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2);
42447       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2);
42448       if (ny0>=height() || ny2<0) return *this;
42449       _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y,
42450                           nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) {
42451         int
42452           xleft = xleft0, xright = xright0,
42453           txleft = txleft0, txright = txright0,
42454           tyleft = tyleft0, tyright = tyright0;
42455         if (xright<xleft) cimg::swap(xleft,xright,txleft,txright,tyleft,tyright);
42456         const int
42457           dx = xright - xleft,
42458           dtx = txright>txleft?txright - txleft:txleft - txright,
42459           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
42460           rtx = dx?(txright - txleft)/dx:0,
42461           rty = dx?(tyright - tyleft)/dx:0,
42462           stx = txright>txleft?1:-1,
42463           sty = tyright>tyleft?1:-1,
42464           ndtx = dtx - (dx?dx*(dtx/dx):0),
42465           ndty = dty - (dx?dx*(dty/dx):0);
42466         int errtx = dx>>1, errty = errtx;
42467         if (xleft<0 && dx) {
42468           txleft-=xleft*(txright - txleft)/dx;
42469           tyleft-=xleft*(tyright - tyleft)/dx;
42470         }
42471         if (xleft<0) xleft = 0;
42472         if (xright>=width() - 1) xright = width() - 1;
42473         T* ptrd = data(xleft,y,0,0);
42474         if (opacity>=1) {
42475           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
42476             const tc *col = &texture._atXY(txleft,tyleft);
42477             cimg_forC(*this,c) {
42478               *ptrd = (T)*col;
42479               ptrd+=whd; col+=twh;
42480             }
42481             ptrd-=offx;
42482             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
42483             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
42484           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
42485             const tc *col = &texture._atXY(txleft,tyleft);
42486             cimg_forC(*this,c) {
42487               *ptrd = (T)(nbrightness**col);
42488               ptrd+=whd; col+=twh;
42489             }
42490             ptrd-=offx;
42491             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
42492             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
42493           } else for (int x = xleft; x<=xright; ++x) {
42494             const tc *col = &texture._atXY(txleft,tyleft);
42495             cimg_forC(*this,c) {
42496               *ptrd = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval);
42497               ptrd+=whd; col+=twh;
42498             }
42499             ptrd-=offx;
42500             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
42501             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
42502           }
42503         } else {
42504           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
42505             const tc *col = &texture._atXY(txleft,tyleft);
42506             cimg_forC(*this,c) {
42507               *ptrd = (T)(nopacity**col + *ptrd*copacity);
42508               ptrd+=whd; col+=twh;
42509             }
42510             ptrd-=offx;
42511             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
42512             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
42513           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
42514             const tc *col = &texture._atXY(txleft,tyleft);
42515             cimg_forC(*this,c) {
42516               *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
42517               ptrd+=whd; col+=twh;
42518             }
42519             ptrd-=offx;
42520             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
42521             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
42522           } else for (int x = xleft; x<=xright; ++x) {
42523             const tc *col = &texture._atXY(txleft,tyleft);
42524             cimg_forC(*this,c) {
42525               const T val = (T)((2 - nbrightness)**(col++) + (nbrightness - 1)*maxval);
42526               *ptrd = (T)(nopacity*val + *ptrd*copacity);
42527               ptrd+=whd; col+=twh;
42528             }
42529             ptrd-=offx;
42530             txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
42531             tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
42532           }
42533         }
42534       }
42535       return *this;
42536     }
42537 
42538     //! Draw a 2d textured triangle, with perspective correction.
42539     template<typename tc>
42540     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
42541                            const int x1, const int y1, const float z1,
42542                            const int x2, const int y2, const float z2,
42543                            const CImg<tc>& texture,
42544                            const int tx0, const int ty0,
42545                            const int tx1, const int ty1,
42546                            const int tx2, const int ty2,
42547                            const float opacity=1,
42548                            const float brightness=1) {
42549       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
42550       if (texture._depth>1 || texture._spectrum<_spectrum)
42551         throw CImgArgumentException(_cimg_instance
42552                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
42553                                     cimg_instance,
42554                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
42555       if (is_overlapped(texture))
42556         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
42557       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
42558       const float
42559         nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f),
42560         nbrightness = cimg::cut(brightness,0,2);
42561       const ulongT
42562         whd = (ulongT)_width*_height*_depth,
42563         twh = (ulongT)texture._width*texture._height,
42564         offx = _spectrum*whd - 1;
42565       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
42566       float
42567         ntx0 = tx0/z0, nty0 = ty0/z0,
42568         ntx1 = tx1/z1, nty1 = ty1/z1,
42569         ntx2 = tx2/z2, nty2 = ty2/z2,
42570         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
42571       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
42572       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
42573       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
42574       if (ny0>=height() || ny2<0) return *this;
42575       float
42576         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
42577         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
42578         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
42579         ptyl = (nty1 - nty0)/(ny1 - ny0),
42580         ptyr = (nty2 - nty0)/(ny2 - ny0),
42581         ptyn = (nty2 - nty1)/(ny2 - ny1),
42582         pzl = (nz1 - nz0)/(ny1 - ny0),
42583         pzr = (nz2 - nz0)/(ny2 - ny0),
42584         pzn = (nz2 - nz1)/(ny2 - ny1),
42585         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
42586         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
42587         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
42588         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
42589         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
42590           (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
42591         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
42592           (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
42593       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
42594         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
42595         int xleft = xleft0, xright = xright0;
42596         float
42597           zleft = zl, zright = zr,
42598           txleft = txl, txright = txr,
42599           tyleft = tyl, tyright = tyr;
42600         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
42601         const int dx = xright - xleft;
42602         const float
42603           pentez = (zright - zleft)/dx,
42604           pentetx = (txright - txleft)/dx,
42605           pentety = (tyright - tyleft)/dx;
42606         if (xleft<0 && dx) {
42607           zleft-=xleft*(zright - zleft)/dx;
42608           txleft-=xleft*(txright - txleft)/dx;
42609           tyleft-=xleft*(tyright - tyleft)/dx;
42610         }
42611         if (xleft<0) xleft = 0;
42612         if (xright>=width() - 1) xright = width() - 1;
42613         T* ptrd = data(xleft,y,0,0);
42614         if (opacity>=1) {
42615           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
42616             const float invz = 1/zleft;
42617             const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42618             cimg_forC(*this,c) {
42619               *ptrd = (T)*col;
42620               ptrd+=whd; col+=twh;
42621             }
42622             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42623           } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) {
42624             const float invz = 1/zleft;
42625             const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42626             cimg_forC(*this,c) {
42627               *ptrd = (T)(nbrightness**col);
42628               ptrd+=whd; col+=twh;
42629             }
42630             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42631           } else for (int x = xleft; x<=xright; ++x) {
42632             const float invz = 1/zleft;
42633             const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42634             cimg_forC(*this,c) {
42635               *ptrd = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval);
42636               ptrd+=whd; col+=twh;
42637             }
42638             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42639           }
42640         } else {
42641           if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
42642             const float invz = 1/zleft;
42643             const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42644             cimg_forC(*this,c) {
42645               *ptrd = (T)(nopacity**col + *ptrd*copacity);
42646               ptrd+=whd; col+=twh;
42647             }
42648             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42649           } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
42650             const float invz = 1/zleft;
42651             const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42652             cimg_forC(*this,c) {
42653               *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
42654               ptrd+=whd; col+=twh;
42655             }
42656             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42657           } else for (int x = xleft; x<=xright; ++x) {
42658             const float invz = 1/zleft;
42659             const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42660             cimg_forC(*this,c) {
42661               const T val = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval);
42662               *ptrd = (T)(nopacity*val + *ptrd*copacity);
42663               ptrd+=whd; col+=twh;
42664             }
42665             ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42666           }
42667         }
42668         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
42669       }
42670       return *this;
42671     }
42672 
42673     //! Draw a textured 2d triangle, with perspective correction and z-buffering.
42674     template<typename tz, typename tc>
42675     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
42676                            const int x0, const int y0, const float z0,
42677                            const int x1, const int y1, const float z1,
42678                            const int x2, const int y2, const float z2,
42679                            const CImg<tc>& texture,
42680                            const int tx0, const int ty0,
42681                            const int tx1, const int ty1,
42682                            const int tx2, const int ty2,
42683                            const float opacity=1,
42684                            const float brightness=1) {
42685       typedef typename cimg::superset<tz,float>::type tzfloat;
42686       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
42687       if (!is_sameXY(zbuffer))
42688         throw CImgArgumentException(_cimg_instance
42689                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
42690                                     "different dimensions.",
42691                                     cimg_instance,
42692                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
42693 
42694       if (texture._depth>1 || texture._spectrum<_spectrum)
42695         throw CImgArgumentException(_cimg_instance
42696                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
42697                                     cimg_instance,
42698                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
42699       if (is_overlapped(texture))
42700         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
42701       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
42702       const float
42703         nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f),
42704         nbrightness = cimg::cut(brightness,0,2);
42705       const ulongT
42706         whd = (ulongT)_width*_height*_depth,
42707         twh = (ulongT)texture._width*texture._height,
42708         offx = _spectrum*whd;
42709       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
42710       float
42711         ntx0 = tx0/z0, nty0 = ty0/z0,
42712         ntx1 = tx1/z1, nty1 = ty1/z1,
42713         ntx2 = tx2/z2, nty2 = ty2/z2;
42714       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
42715       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
42716       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
42717       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
42718       if (ny0>=height() || ny2<0) return *this;
42719       float
42720         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
42721         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
42722         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
42723         ptyl = (nty1 - nty0)/(ny1 - ny0),
42724         ptyr = (nty2 - nty0)/(ny2 - ny0),
42725         ptyn = (nty2 - nty1)/(ny2 - ny1),
42726         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
42727         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
42728         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
42729           (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
42730         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
42731           (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
42732       tzfloat
42733         pzl = (nz1 - nz0)/(ny1 - ny0),
42734         pzr = (nz2 - nz0)/(ny2 - ny0),
42735         pzn = (nz2 - nz1)/(ny2 - ny1),
42736         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
42737         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
42738       _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
42739         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
42740         int xleft = xleft0, xright = xright0;
42741         float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
42742         tzfloat zleft = zl, zright = zr;
42743         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
42744         const int dx = xright - xleft;
42745         const float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
42746         const tzfloat pentez = (zright - zleft)/dx;
42747         if (xleft<0 && dx) {
42748           zleft-=xleft*(zright - zleft)/dx;
42749           txleft-=xleft*(txright - txleft)/dx;
42750           tyleft-=xleft*(tyright - tyleft)/dx;
42751         }
42752         if (xleft<0) xleft = 0;
42753         if (xright>=width() - 1) xright = width() - 1;
42754         T *ptrd = data(xleft,y,0,0);
42755         tz *ptrz = zbuffer.data(xleft,y);
42756         if (opacity>=1) {
42757           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42758               if (zleft>=(tzfloat)*ptrz) {
42759                 *ptrz = (tz)zleft;
42760                 const tzfloat invz = 1/zleft;
42761                 const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42762                 cimg_forC(*this,c) {
42763                   *ptrd = (T)*col;
42764                   ptrd+=whd; col+=twh;
42765                 }
42766                 ptrd-=offx;
42767               }
42768               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42769             } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42770               if (zleft>=(tzfloat)*ptrz) {
42771                 *ptrz = (tz)zleft;
42772                 const tzfloat invz = 1/zleft;
42773                 const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42774                 cimg_forC(*this,c) {
42775                   *ptrd = (T)(nbrightness**col);
42776                   ptrd+=whd; col+=twh;
42777                 }
42778                 ptrd-=offx;
42779               }
42780               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42781             } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42782               if (zleft>=(tzfloat)*ptrz) {
42783                 *ptrz = (tz)zleft;
42784                 const tzfloat invz = 1/zleft;
42785                 const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42786                 cimg_forC(*this,c) {
42787                   *ptrd = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval);
42788                   ptrd+=whd; col+=twh;
42789                 }
42790                 ptrd-=offx;
42791               }
42792               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42793             }
42794         } else {
42795           if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42796               if (zleft>=(tzfloat)*ptrz) {
42797                 *ptrz = (tz)zleft;
42798                 const tzfloat invz = 1/zleft;
42799                 const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42800                 cimg_forC(*this,c) {
42801                   *ptrd = (T)(nopacity**col + *ptrd*copacity);
42802                   ptrd+=whd; col+=twh;
42803                 }
42804                 ptrd-=offx;
42805               }
42806               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42807             } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42808               if (zleft>=(tzfloat)*ptrz) {
42809                 *ptrz = (tz)zleft;
42810                 const tzfloat invz = 1/zleft;
42811                 const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42812                 cimg_forC(*this,c) {
42813                   *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
42814                   ptrd+=whd; col+=twh;
42815                 }
42816                 ptrd-=offx;
42817               }
42818               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42819             } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
42820               if (zleft>=(tzfloat)*ptrz) {
42821                 *ptrz = (tz)zleft;
42822                 const tzfloat invz = 1/zleft;
42823                 const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
42824                 cimg_forC(*this,c) {
42825                   const T val = (T)((2 - nbrightness)**col + (nbrightness - 1)*maxval);
42826                   *ptrd = (T)(nopacity*val + *ptrd*copacity);
42827                   ptrd+=whd; col+=twh;
42828                 }
42829                 ptrd-=offx;
42830               }
42831               zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
42832             }
42833         }
42834         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
42835       }
42836       return *this;
42837     }
42838 
42839     //! Draw a Phong-shaded 2d triangle.
42840     /**
42841        \param x0 X-coordinate of the first vertex in the image instance.
42842        \param y0 Y-coordinate of the first vertex in the image instance.
42843        \param x1 X-coordinate of the second vertex in the image instance.
42844        \param y1 Y-coordinate of the second vertex in the image instance.
42845        \param x2 X-coordinate of the third vertex in the image instance.
42846        \param y2 Y-coordinate of the third vertex in the image instance.
42847        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
42848        \param light Light image.
42849        \param lx0 X-coordinate of the first vertex in the light image.
42850        \param ly0 Y-coordinate of the first vertex in the light image.
42851        \param lx1 X-coordinate of the second vertex in the light image.
42852        \param ly1 Y-coordinate of the second vertex in the light image.
42853        \param lx2 X-coordinate of the third vertex in the light image.
42854        \param ly2 Y-coordinate of the third vertex in the light image.
42855        \param opacity Drawing opacity.
42856     **/
42857     template<typename tc, typename tl>
42858     CImg<T>& draw_triangle(const int x0, const int y0,
42859                            const int x1, const int y1,
42860                            const int x2, const int y2,
42861                            const tc *const color,
42862                            const CImg<tl>& light,
42863                            const int lx0, const int ly0,
42864                            const int lx1, const int ly1,
42865                            const int lx2, const int ly2,
42866                            const float opacity=1) {
42867       if (is_empty()) return *this;
42868       if (!color)
42869         throw CImgArgumentException(_cimg_instance
42870                                     "draw_triangle(): Specified color is (null).",
42871                                     cimg_instance);
42872       if (light._depth>1 || light._spectrum<_spectrum)
42873         throw CImgArgumentException(_cimg_instance
42874                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
42875                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
42876       if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
42877       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
42878       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
42879       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
42880         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
42881       const ulongT
42882         whd = (ulongT)_width*_height*_depth,
42883         lwh = (ulongT)light._width*light._height,
42884         offx = _spectrum*whd - 1;
42885       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1);
42886       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2);
42887       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2);
42888       if (ny0>=height() || ny2<0) return *this;
42889       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
42890                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
42891         int
42892           xleft = xleft0, xright = xright0,
42893           lxleft = lxleft0, lxright = lxright0,
42894           lyleft = lyleft0, lyright = lyright0;
42895         if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright);
42896         const int
42897           dx = xright - xleft,
42898           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
42899           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
42900           rlx = dx?(lxright - lxleft)/dx:0,
42901           rly = dx?(lyright - lyleft)/dx:0,
42902           slx = lxright>lxleft?1:-1,
42903           sly = lyright>lyleft?1:-1,
42904           ndlx = dlx - (dx?dx*(dlx/dx):0),
42905           ndly = dly - (dx?dx*(dly/dx):0);
42906         int errlx = dx>>1, errly = errlx;
42907         if (xleft<0 && dx) {
42908           lxleft-=xleft*(lxright - lxleft)/dx;
42909           lyleft-=xleft*(lyright - lyleft)/dx;
42910         }
42911         if (xleft<0) xleft = 0;
42912         if (xright>=width() - 1) xright = width() - 1;
42913         T* ptrd = data(xleft,y,0,0);
42914         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
42915           const tc *col = color;
42916           const tl *lig = &light._atXY(lxleft,lyleft);
42917           cimg_forC(*this,c) {
42918             const tl l = *lig;
42919             *ptrd = (T)(l<1?l**(col++):((2 - l)**(col++) + (l - 1)*maxval));
42920             ptrd+=whd; lig+=lwh;
42921           }
42922           ptrd-=offx;
42923           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
42924           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
42925         } else  for (int x = xleft; x<=xright; ++x) {
42926           const tc *col = color;
42927           const tl *lig = &light._atXY(lxleft,lyleft);
42928           cimg_forC(*this,c) {
42929             const tl l = *lig;
42930             const T val = (T)(l<1?l**(col++):((2 - l)**(col++) + (l - 1)*maxval));
42931             *ptrd = (T)(nopacity*val + *ptrd*copacity);
42932             ptrd+=whd; lig+=lwh;
42933           }
42934           ptrd-=offx;
42935           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
42936           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
42937         }
42938       }
42939       return *this;
42940     }
42941 
42942     //! Draw a Phong-shaded 2d triangle, with z-buffering.
42943     template<typename tz, typename tc, typename tl>
42944     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
42945                            const int x0, const int y0, const float z0,
42946                            const int x1, const int y1, const float z1,
42947                            const int x2, const int y2, const float z2,
42948                            const tc *const color,
42949                            const CImg<tl>& light,
42950                            const int lx0, const int ly0,
42951                            const int lx1, const int ly1,
42952                            const int lx2, const int ly2,
42953                            const float opacity=1) {
42954       typedef typename cimg::superset<tz,float>::type tzfloat;
42955       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
42956       if (!color)
42957         throw CImgArgumentException(_cimg_instance
42958                                     "draw_triangle(): Specified color is (null).",
42959                                     cimg_instance);
42960       if (light._depth>1 || light._spectrum<_spectrum)
42961         throw CImgArgumentException(_cimg_instance
42962                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
42963                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
42964       if (!is_sameXY(zbuffer))
42965         throw CImgArgumentException(_cimg_instance
42966                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
42967                                     "different dimensions.",
42968                                     cimg_instance,
42969                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
42970       if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
42971                                                      +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
42972       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
42973       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
42974       const ulongT
42975         whd = (ulongT)_width*_height*_depth,
42976         lwh = (ulongT)light._width*light._height,
42977         offx = _spectrum*whd;
42978       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
42979         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
42980       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
42981       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1);
42982       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2);
42983       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2);
42984       if (ny0>=height() || ny2<0) return *this;
42985       tzfloat
42986         pzl = (nz1 - nz0)/(ny1 - ny0),
42987         pzr = (nz2 - nz0)/(ny2 - ny0),
42988         pzn = (nz2 - nz1)/(ny2 - ny1),
42989         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
42990         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
42991       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
42992                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
42993         if (y==ny1) { zl = nz1; pzl = pzn; }
42994         int
42995           xleft = xleft0, xright = xright0,
42996           lxleft = lxleft0, lxright = lxright0,
42997           lyleft = lyleft0, lyright = lyright0;
42998         tzfloat zleft = zl, zright = zr;
42999         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,lxleft,lxright,lyleft,lyright);
43000         const int
43001           dx = xright - xleft,
43002           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
43003           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
43004           rlx = dx?(lxright - lxleft)/dx:0,
43005           rly = dx?(lyright - lyleft)/dx:0,
43006           slx = lxright>lxleft?1:-1,
43007           sly = lyright>lyleft?1:-1,
43008           ndlx = dlx - (dx?dx*(dlx/dx):0),
43009           ndly = dly - (dx?dx*(dly/dx):0);
43010         const tzfloat pentez = (zright - zleft)/dx;
43011         int errlx = dx>>1, errly = errlx;
43012         if (xleft<0 && dx) {
43013           zleft-=xleft*(zright - zleft)/dx;
43014           lxleft-=xleft*(lxright - lxleft)/dx;
43015           lyleft-=xleft*(lyright - lyleft)/dx;
43016         }
43017         if (xleft<0) xleft = 0;
43018         if (xright>=width() - 1) xright = width() - 1;
43019         T *ptrd = data(xleft,y,0,0);
43020         tz *ptrz = xleft<=xright?zbuffer.data(xleft,y):0;
43021         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
43022             if (zleft>=(tzfloat)*ptrz) {
43023               *ptrz = (tz)zleft;
43024               const tc *col = color;
43025               const tl *lig = &light._atXY(lxleft,lyleft);
43026               cimg_forC(*this,c) {
43027                 const tl l = *lig;
43028                 const tc cval = *(col++);
43029                 *ptrd = (T)(l<1?l*cval:(2 - l)*cval + (l - 1)*maxval);
43030                 ptrd+=whd; lig+=lwh;
43031               }
43032               ptrd-=offx;
43033             }
43034             zleft+=pentez;
43035             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
43036             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
43037           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
43038             if (zleft>=(tzfloat)*ptrz) {
43039               *ptrz = (tz)zleft;
43040               const tc *col = color;
43041               const tl *lig = &light._atXY(lxleft,lyleft);
43042               cimg_forC(*this,c) {
43043                 const tl l = *lig;
43044                 const tc cval = *(col++);
43045                 const T val = (T)(l<1?l*cval:(2 - l)*cval + (l - 1)*maxval);
43046                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
43047                 ptrd+=whd; lig+=lwh;
43048               }
43049               ptrd-=offx;
43050             }
43051             zleft+=pentez;
43052             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
43053             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
43054           }
43055         zr+=pzr; zl+=pzl;
43056       }
43057       return *this;
43058     }
43059 
43060     //! Draw a textured Gouraud-shaded 2d triangle.
43061     /**
43062        \param x0 X-coordinate of the first vertex in the image instance.
43063        \param y0 Y-coordinate of the first vertex in the image instance.
43064        \param x1 X-coordinate of the second vertex in the image instance.
43065        \param y1 Y-coordinate of the second vertex in the image instance.
43066        \param x2 X-coordinate of the third vertex in the image instance.
43067        \param y2 Y-coordinate of the third vertex in the image instance.
43068        \param texture Texture image used to fill the triangle.
43069        \param tx0 X-coordinate of the first vertex in the texture image.
43070        \param ty0 Y-coordinate of the first vertex in the texture image.
43071        \param tx1 X-coordinate of the second vertex in the texture image.
43072        \param ty1 Y-coordinate of the second vertex in the texture image.
43073        \param tx2 X-coordinate of the third vertex in the texture image.
43074        \param ty2 Y-coordinate of the third vertex in the texture image.
43075        \param brightness0 Brightness factor of the first vertex.
43076        \param brightness1 Brightness factor of the second vertex.
43077        \param brightness2 Brightness factor of the third vertex.
43078        \param opacity Drawing opacity.
43079     **/
43080     template<typename tc>
43081     CImg<T>& draw_triangle(const int x0, const int y0,
43082                            const int x1, const int y1,
43083                            const int x2, const int y2,
43084                            const CImg<tc>& texture,
43085                            const int tx0, const int ty0,
43086                            const int tx1, const int ty1,
43087                            const int tx2, const int ty2,
43088                            const float brightness0,
43089                            const float brightness1,
43090                            const float brightness2,
43091                            const float opacity=1) {
43092       if (is_empty()) return *this;
43093       if (texture._depth>1 || texture._spectrum<_spectrum)
43094         throw CImgArgumentException(_cimg_instance
43095                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
43096                                     cimg_instance,
43097                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
43098       if (is_overlapped(texture))
43099         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
43100                              brightness0,brightness1,brightness2,opacity);
43101       static const T maxval = (T)std::min(cimg::type<T>::max(),cimg::type<tc>::max());
43102       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
43103       const ulongT
43104         whd = (ulongT)_width*_height*_depth,
43105         twh = (ulongT)texture._width*texture._height,
43106         offx = _spectrum*whd - 1;
43107       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
43108         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
43109         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
43110         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
43111         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
43112       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1);
43113       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2);
43114       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2);
43115       if (ny0>=height() || ny2<0) return *this;
43116       _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y,
43117                           nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) {
43118         int
43119           xleft = xleft0, xright = xright0,
43120           cleft = cleft0, cright = cright0,
43121           txleft = txleft0, txright = txright0,
43122           tyleft = tyleft0, tyright = tyright0;
43123         if (xright<xleft) cimg::swap(xleft,xright,cleft,cright,txleft,txright,tyleft,tyright);
43124         const int
43125           dx = xright - xleft,
43126           dc = cright>cleft?cright - cleft:cleft - cright,
43127           dtx = txright>txleft?txright - txleft:txleft - txright,
43128           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
43129           rc = dx?(cright - cleft)/dx:0,
43130           rtx = dx?(txright - txleft)/dx:0,
43131           rty = dx?(tyright - tyleft)/dx:0,
43132           sc = cright>cleft?1:-1,
43133           stx = txright>txleft?1:-1,
43134           sty = tyright>tyleft?1:-1,
43135           ndc = dc - (dx?dx*(dc/dx):0),
43136           ndtx = dtx - (dx?dx*(dtx/dx):0),
43137           ndty = dty - (dx?dx*(dty/dx):0);
43138         int errc = dx>>1, errtx = errc, errty = errc;
43139         if (xleft<0 && dx) {
43140           cleft-=xleft*(cright - cleft)/dx;
43141           txleft-=xleft*(txright - txleft)/dx;
43142           tyleft-=xleft*(tyright - tyleft)/dx;
43143         }
43144         if (xleft<0) xleft = 0;
43145         if (xright>=width() - 1) xright = width() - 1;
43146         T* ptrd = data(xleft,y,0,0);
43147         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
43148           const tc *col = &texture._atXY(txleft,tyleft);
43149           cimg_forC(*this,c) {
43150             *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
43151             ptrd+=whd; col+=twh;
43152           }
43153           ptrd-=offx;
43154           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
43155           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
43156           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
43157         } else for (int x = xleft; x<=xright; ++x) {
43158           const tc *col = &texture._atXY(txleft,tyleft);
43159           cimg_forC(*this,c) {
43160             const T val = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
43161             *ptrd = (T)(nopacity*val + *ptrd*copacity);
43162             ptrd+=whd; col+=twh;
43163           }
43164           ptrd-=offx;
43165           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
43166           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
43167           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
43168         }
43169       }
43170       return *this;
43171     }
43172 
43173     //! Draw a textured Gouraud-shaded 2d triangle, with perspective correction \overloading.
43174     template<typename tc>
43175     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
43176                            const int x1, const int y1, const float z1,
43177                            const int x2, const int y2, const float z2,
43178                            const CImg<tc>& texture,
43179                            const int tx0, const int ty0,
43180                            const int tx1, const int ty1,
43181                            const int tx2, const int ty2,
43182                            const float brightness0,
43183                            const float brightness1,
43184                            const float brightness2,
43185                            const float opacity=1) {
43186       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
43187       if (texture._depth>1 || texture._spectrum<_spectrum)
43188         throw CImgArgumentException(_cimg_instance
43189                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
43190                                     cimg_instance,
43191                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
43192       if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
43193                                                        brightness0,brightness1,brightness2,opacity);
43194       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
43195       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
43196       const ulongT
43197         whd = (ulongT)_width*_height*_depth,
43198         twh = (ulongT)texture._width*texture._height,
43199         offx = _spectrum*whd - 1;
43200       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
43201         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
43202         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
43203         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
43204       float
43205         ntx0 = tx0/z0, nty0 = ty0/z0,
43206         ntx1 = tx1/z1, nty1 = ty1/z1,
43207         ntx2 = tx2/z2, nty2 = ty2/z2,
43208         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
43209       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
43210       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
43211       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
43212       if (ny0>=height() || ny2<0) return *this;
43213       float
43214         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
43215         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
43216         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
43217         ptyl = (nty1 - nty0)/(ny1 - ny0),
43218         ptyr = (nty2 - nty0)/(ny2 - ny0),
43219         ptyn = (nty2 - nty1)/(ny2 - ny1),
43220         pzl = (nz1 - nz0)/(ny1 - ny0),
43221         pzr = (nz2 - nz0)/(ny2 - ny0),
43222         pzn = (nz2 - nz1)/(ny2 - ny1),
43223         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
43224         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
43225         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
43226         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
43227         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
43228           (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
43229         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
43230           (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
43231       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
43232         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
43233         int
43234           xleft = xleft0, xright = xright0,
43235           cleft = cleft0, cright = cright0;
43236         float
43237           zleft = zl, zright = zr,
43238           txleft = txl, txright = txr,
43239           tyleft = tyl, tyright = tyr;
43240         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
43241         const int
43242           dx = xright - xleft,
43243           dc = cright>cleft?cright - cleft:cleft - cright,
43244           rc = dx?(cright - cleft)/dx:0,
43245           sc = cright>cleft?1:-1,
43246           ndc = dc - (dx?dx*(dc/dx):0);
43247         const float
43248           pentez = (zright - zleft)/dx,
43249           pentetx = (txright - txleft)/dx,
43250           pentety = (tyright - tyleft)/dx;
43251         int errc = dx>>1;
43252         if (xleft<0 && dx) {
43253           cleft-=xleft*(cright - cleft)/dx;
43254           zleft-=xleft*(zright - zleft)/dx;
43255           txleft-=xleft*(txright - txleft)/dx;
43256           tyleft-=xleft*(tyright - tyleft)/dx;
43257         }
43258         if (xleft<0) xleft = 0;
43259         if (xright>=width() - 1) xright = width() - 1;
43260         T* ptrd = data(xleft,y,0,0);
43261         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
43262           const float invz = 1/zleft;
43263           const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
43264           cimg_forC(*this,c) {
43265             *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
43266             ptrd+=whd; col+=twh;
43267           }
43268           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
43269           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
43270         } else for (int x = xleft; x<=xright; ++x) {
43271           const float invz = 1/zleft;
43272           const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
43273           cimg_forC(*this,c) {
43274             const T val = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
43275             *ptrd = (T)(nopacity*val + *ptrd*copacity);
43276             ptrd+=whd; col+=twh;
43277           }
43278           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
43279           cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
43280         }
43281         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
43282       }
43283       return *this;
43284     }
43285 
43286     //! Draw a textured Gouraud-shaded 2d triangle, with perspective correction and z-buffering \overloading.
43287     template<typename tz, typename tc>
43288     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
43289                            const int x0, const int y0, const float z0,
43290                            const int x1, const int y1, const float z1,
43291                            const int x2, const int y2, const float z2,
43292                            const CImg<tc>& texture,
43293                            const int tx0, const int ty0,
43294                            const int tx1, const int ty1,
43295                            const int tx2, const int ty2,
43296                            const float brightness0,
43297                            const float brightness1,
43298                            const float brightness2,
43299                            const float opacity=1) {
43300       typedef typename cimg::superset<tz,float>::type tzfloat;
43301       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
43302       if (!is_sameXY(zbuffer))
43303         throw CImgArgumentException(_cimg_instance
43304                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
43305                                     "different dimensions.",
43306                                     cimg_instance,
43307                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
43308       if (texture._depth>1 || texture._spectrum<_spectrum)
43309         throw CImgArgumentException(_cimg_instance
43310                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
43311                                     cimg_instance,
43312                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
43313       if (is_overlapped(texture))
43314         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
43315                                                        brightness0,brightness1,brightness2,opacity);
43316       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
43317       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
43318       const ulongT
43319         whd = (ulongT)_width*_height*_depth,
43320         twh = (ulongT)texture._width*texture._height,
43321         offx = _spectrum*whd;
43322       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
43323         nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
43324         nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
43325         nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
43326       float
43327         ntx0 = tx0/z0, nty0 = ty0/z0,
43328         ntx1 = tx1/z1, nty1 = ty1/z1,
43329         ntx2 = tx2/z2, nty2 = ty2/z2;
43330       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
43331       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
43332       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
43333       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
43334       if (ny0>=height() || ny2<0) return *this;
43335       float
43336         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
43337         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
43338         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
43339         ptyl = (nty1 - nty0)/(ny1 - ny0),
43340         ptyr = (nty2 - nty0)/(ny2 - ny0),
43341         ptyn = (nty2 - nty1)/(ny2 - ny1),
43342         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
43343         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
43344         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
43345           (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
43346         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
43347           (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
43348       tzfloat
43349         pzl = (nz1 - nz0)/(ny1 - ny0),
43350         pzr = (nz2 - nz0)/(ny2 - ny0),
43351         pzn = (nz2 - nz1)/(ny2 - ny1),
43352         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
43353         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
43354       _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
43355         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
43356         int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
43357         float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
43358         tzfloat zleft = zl, zright = zr;
43359         if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
43360         const int
43361           dx = xright - xleft,
43362           dc = cright>cleft?cright - cleft:cleft - cright,
43363           rc = dx?(cright - cleft)/dx:0,
43364           sc = cright>cleft?1:-1,
43365           ndc = dc - (dx?dx*(dc/dx):0);
43366         float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
43367         const tzfloat pentez = (zright - zleft)/dx;
43368         int errc = dx>>1;
43369         if (xleft<0 && dx) {
43370           cleft-=xleft*(cright - cleft)/dx;
43371           zleft-=xleft*(zright - zleft)/dx;
43372           txleft-=xleft*(txright - txleft)/dx;
43373           tyleft-=xleft*(tyright - tyleft)/dx;
43374         }
43375         if (xleft<0) xleft = 0;
43376         if (xright>=width() - 1) xright = width() - 1;
43377         T* ptrd = data(xleft,y);
43378         tz *ptrz = zbuffer.data(xleft,y);
43379         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
43380             if (zleft>=(tzfloat)*ptrz) {
43381               *ptrz = (tz)zleft;
43382               const tzfloat invz = 1/zleft;
43383               const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
43384               cimg_forC(*this,c) {
43385                 *ptrd = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
43386                 ptrd+=whd; col+=twh;
43387               }
43388               ptrd-=offx;
43389             }
43390             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
43391             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
43392           } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
43393             if (zleft>=(tzfloat)*ptrz) {
43394               *ptrz = (tz)zleft;
43395               const tzfloat invz = 1/zleft;
43396               const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
43397               cimg_forC(*this,c) {
43398                 const T val = (T)(cleft<256?cleft**col/256:((512 - cleft)**col + (cleft - 256)*maxval)/256);
43399                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
43400                 ptrd+=whd; col+=twh;
43401               }
43402               ptrd-=offx;
43403             }
43404             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
43405             cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
43406           }
43407         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
43408       }
43409       return *this;
43410     }
43411 
43412     //! Draw a textured Phong-shaded 2d triangle.
43413     /**
43414        \param x0 X-coordinate of the first vertex in the image instance.
43415        \param y0 Y-coordinate of the first vertex in the image instance.
43416        \param x1 X-coordinate of the second vertex in the image instance.
43417        \param y1 Y-coordinate of the second vertex in the image instance.
43418        \param x2 X-coordinate of the third vertex in the image instance.
43419        \param y2 Y-coordinate of the third vertex in the image instance.
43420        \param texture Texture image used to fill the triangle.
43421        \param tx0 X-coordinate of the first vertex in the texture image.
43422        \param ty0 Y-coordinate of the first vertex in the texture image.
43423        \param tx1 X-coordinate of the second vertex in the texture image.
43424        \param ty1 Y-coordinate of the second vertex in the texture image.
43425        \param tx2 X-coordinate of the third vertex in the texture image.
43426        \param ty2 Y-coordinate of the third vertex in the texture image.
43427        \param light Light image.
43428        \param lx0 X-coordinate of the first vertex in the light image.
43429        \param ly0 Y-coordinate of the first vertex in the light image.
43430        \param lx1 X-coordinate of the second vertex in the light image.
43431        \param ly1 Y-coordinate of the second vertex in the light image.
43432        \param lx2 X-coordinate of the third vertex in the light image.
43433        \param ly2 Y-coordinate of the third vertex in the light image.
43434        \param opacity Drawing opacity.
43435     **/
43436     template<typename tc, typename tl>
43437     CImg<T>& draw_triangle(const int x0, const int y0,
43438                            const int x1, const int y1,
43439                            const int x2, const int y2,
43440                            const CImg<tc>& texture,
43441                            const int tx0, const int ty0,
43442                            const int tx1, const int ty1,
43443                            const int tx2, const int ty2,
43444                            const CImg<tl>& light,
43445                            const int lx0, const int ly0,
43446                            const int lx1, const int ly1,
43447                            const int lx2, const int ly2,
43448                            const float opacity=1) {
43449       if (is_empty()) return *this;
43450       if (texture._depth>1 || texture._spectrum<_spectrum)
43451         throw CImgArgumentException(_cimg_instance
43452                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
43453                                     cimg_instance,
43454                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
43455       if (light._depth>1 || light._spectrum<_spectrum)
43456         throw CImgArgumentException(_cimg_instance
43457                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
43458                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
43459       if (is_overlapped(texture))
43460         return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
43461       if (is_overlapped(light))
43462         return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
43463       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
43464       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
43465       const ulongT
43466         whd = (ulongT)_width*_height*_depth,
43467         twh = (ulongT)texture._width*texture._height,
43468         lwh = (ulongT)light._width*light._height,
43469         offx = _spectrum*whd - 1;
43470       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
43471         ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
43472         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
43473       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1);
43474       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2);
43475       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2);
43476       if (ny0>=height() || ny2<0) return *this;
43477       const bool is_bump = texture._spectrum>=_spectrum + 2;
43478       const ulongT obx = twh*_spectrum, oby = twh*(_spectrum + 1);
43479 
43480       _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y,
43481                           nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) {
43482         int
43483           xleft = xleft0, xright = xright0,
43484           lxleft = lxleft0, lxright = lxright0,
43485           lyleft = lyleft0, lyright = lyright0,
43486           txleft = txleft0, txright = txright0,
43487           tyleft = tyleft0, tyright = tyright0;
43488         if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright,txleft,txright,tyleft,tyright);
43489         const int
43490           dx = xright - xleft,
43491           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
43492           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
43493           dtx = txright>txleft?txright - txleft:txleft - txright,
43494           dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
43495           rlx = dx?(lxright - lxleft)/dx:0,
43496           rly = dx?(lyright - lyleft)/dx:0,
43497           rtx = dx?(txright - txleft)/dx:0,
43498           rty = dx?(tyright - tyleft)/dx:0,
43499           slx = lxright>lxleft?1:-1,
43500           sly = lyright>lyleft?1:-1,
43501           stx = txright>txleft?1:-1,
43502           sty = tyright>tyleft?1:-1,
43503           ndlx = dlx - (dx?dx*(dlx/dx):0),
43504           ndly = dly - (dx?dx*(dly/dx):0),
43505           ndtx = dtx - (dx?dx*(dtx/dx):0),
43506           ndty = dty - (dx?dx*(dty/dx):0);
43507         int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx;
43508         if (xleft<0 && dx) {
43509           lxleft-=xleft*(lxright - lxleft)/dx;
43510           lyleft-=xleft*(lyright - lyleft)/dx;
43511           txleft-=xleft*(txright - txleft)/dx;
43512           tyleft-=xleft*(tyright - tyleft)/dx;
43513         }
43514         if (xleft<0) xleft = 0;
43515         if (xright>=width() - 1) xright = width() - 1;
43516         T* ptrd = data(xleft,y,0,0);
43517         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
43518           const tc *col = &texture._atXY(txleft,tyleft);
43519           const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
43520           const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
43521           cimg_forC(*this,c) {
43522             const tl l = *lig;
43523             *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
43524             ptrd+=whd; col+=twh; lig+=lwh;
43525           }
43526           ptrd-=offx;
43527           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
43528           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
43529           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
43530           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
43531         } else for (int x = xleft; x<=xright; ++x) {
43532           const tc *col = &texture._atXY(txleft,tyleft);
43533           const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
43534           const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
43535           cimg_forC(*this,c) {
43536             const tl l = *lig;
43537             const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
43538             *ptrd = (T)(nopacity*val + *ptrd*copacity);
43539             ptrd+=whd; col+=twh; lig+=lwh;
43540           }
43541           ptrd-=offx;
43542           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
43543           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
43544           txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
43545           tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
43546         }
43547       }
43548       return *this;
43549     }
43550 
43551     //! Draw a textured Phong-shaded 2d triangle, with perspective correction.
43552     template<typename tc, typename tl>
43553     CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
43554                            const int x1, const int y1, const float z1,
43555                            const int x2, const int y2, const float z2,
43556                            const CImg<tc>& texture,
43557                            const int tx0, const int ty0,
43558                            const int tx1, const int ty1,
43559                            const int tx2, const int ty2,
43560                            const CImg<tl>& light,
43561                            const int lx0, const int ly0,
43562                            const int lx1, const int ly1,
43563                            const int lx2, const int ly2,
43564                            const float opacity=1) {
43565       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
43566       if (texture._depth>1 || texture._spectrum<_spectrum)
43567         throw CImgArgumentException(_cimg_instance
43568                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
43569                                     cimg_instance,
43570                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
43571       if (light._depth>1 || light._spectrum<_spectrum)
43572         throw CImgArgumentException(_cimg_instance
43573                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
43574                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
43575       if (is_overlapped(texture))
43576         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
43577                              light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
43578       if (is_overlapped(light))
43579         return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,
43580                              +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
43581       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
43582       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
43583       const ulongT
43584         whd = (ulongT)_width*_height*_depth,
43585         twh = (ulongT)texture._width*texture._height,
43586         lwh = (ulongT)light._width*light._height,
43587         offx = _spectrum*whd - 1;
43588       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
43589         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
43590       float
43591         ntx0 = tx0/z0, nty0 = ty0/z0,
43592         ntx1 = tx1/z1, nty1 = ty1/z1,
43593         ntx2 = tx2/z2, nty2 = ty2/z2,
43594         nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
43595       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
43596       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
43597       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
43598       if (ny0>=height() || ny2<0) return *this;
43599       float
43600         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
43601         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
43602         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
43603         ptyl = (nty1 - nty0)/(ny1 - ny0),
43604         ptyr = (nty2 - nty0)/(ny2 - ny0),
43605         ptyn = (nty2 - nty1)/(ny2 - ny1),
43606         pzl = (nz1 - nz0)/(ny1 - ny0),
43607         pzr = (nz2 - nz0)/(ny2 - ny0),
43608         pzn = (nz2 - nz1)/(ny2 - ny1),
43609         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
43610         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
43611         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
43612         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
43613         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
43614           (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
43615         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
43616           (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
43617       const bool is_bump = texture._spectrum>=_spectrum + 2;
43618       const ulongT obx = twh*_spectrum, oby = twh*(_spectrum + 1);
43619 
43620       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
43621                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
43622         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
43623         int
43624           xleft = xleft0, xright = xright0,
43625           lxleft = lxleft0, lxright = lxright0,
43626           lyleft = lyleft0, lyright = lyright0;
43627         float
43628           zleft = zl, zright = zr,
43629           txleft = txl, txright = txr,
43630           tyleft = tyl, tyright = tyr;
43631         if (xright<xleft)
43632           cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
43633         const int
43634           dx = xright - xleft,
43635           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
43636           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
43637           rlx = dx?(lxright - lxleft)/dx:0,
43638           rly = dx?(lyright - lyleft)/dx:0,
43639           slx = lxright>lxleft?1:-1,
43640           sly = lyright>lyleft?1:-1,
43641           ndlx = dlx - (dx?dx*(dlx/dx):0),
43642           ndly = dly - (dx?dx*(dly/dx):0);
43643         const float
43644           pentez = (zright - zleft)/dx,
43645           pentetx = (txright - txleft)/dx,
43646           pentety = (tyright - tyleft)/dx;
43647         int errlx = dx>>1, errly = errlx;
43648         if (xleft<0 && dx) {
43649           zleft-=xleft*(zright - zleft)/dx;
43650           lxleft-=xleft*(lxright - lxleft)/dx;
43651           lyleft-=xleft*(lyright - lyleft)/dx;
43652           txleft-=xleft*(txright - txleft)/dx;
43653           tyleft-=xleft*(tyright - tyleft)/dx;
43654         }
43655         if (xleft<0) xleft = 0;
43656         if (xright>=width() - 1) xright = width() - 1;
43657         T* ptrd = data(xleft,y,0,0);
43658         if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
43659           const float invz = 1/zleft;
43660           const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
43661           const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
43662           const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
43663           cimg_forC(*this,c) {
43664             const tl l = *lig;
43665             *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
43666             ptrd+=whd; col+=twh; lig+=lwh;
43667           }
43668           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
43669           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
43670           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
43671         } else for (int x = xleft; x<=xright; ++x) {
43672           const float invz = 1/zleft;
43673           const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
43674           const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
43675           const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
43676           cimg_forC(*this,c) {
43677             const tl l = *lig;
43678             const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
43679             *ptrd = (T)(nopacity*val + *ptrd*copacity);
43680             ptrd+=whd; col+=twh; lig+=lwh;
43681           }
43682           ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
43683           lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
43684           lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
43685         }
43686         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
43687       }
43688       return *this;
43689     }
43690 
43691     //! Draw a textured Phong-shaded 2d triangle, with perspective correction and z-buffering.
43692     template<typename tz, typename tc, typename tl>
43693     CImg<T>& draw_triangle(CImg<tz>& zbuffer,
43694                            const int x0, const int y0, const float z0,
43695                            const int x1, const int y1, const float z1,
43696                            const int x2, const int y2, const float z2,
43697                            const CImg<tc>& texture,
43698                            const int tx0, const int ty0,
43699                            const int tx1, const int ty1,
43700                            const int tx2, const int ty2,
43701                            const CImg<tl>& light,
43702                            const int lx0, const int ly0,
43703                            const int lx1, const int ly1,
43704                            const int lx2, const int ly2,
43705                            const float opacity=1) {
43706       typedef typename cimg::superset<tz,float>::type tzfloat;
43707       if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
43708       if (!is_sameXY(zbuffer))
43709         throw CImgArgumentException(_cimg_instance
43710                                     "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
43711                                     "different dimensions.",
43712                                     cimg_instance,
43713                                     zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
43714       if (texture._depth>1 || texture._spectrum<_spectrum)
43715         throw CImgArgumentException(_cimg_instance
43716                                     "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
43717                                     cimg_instance,
43718                                     texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
43719       if (light._depth>1 || light._spectrum<_spectrum)
43720         throw CImgArgumentException(_cimg_instance
43721                                     "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
43722                                     cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
43723       if (is_overlapped(texture))
43724         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
43725                              +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
43726       if (is_overlapped(light))
43727         return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
43728                              texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
43729       static const T maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max());
43730       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
43731       const ulongT
43732         whd = (ulongT)_width*_height*_depth,
43733         twh = (ulongT)texture._width*texture._height,
43734         lwh = (ulongT)light._width*light._height,
43735         offx = _spectrum*whd;
43736       int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
43737         nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
43738       float
43739         ntx0 = tx0/z0, nty0 = ty0/z0,
43740         ntx1 = tx1/z1, nty1 = ty1/z1,
43741         ntx2 = tx2/z2, nty2 = ty2/z2;
43742       tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
43743       if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
43744       if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
43745       if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
43746       if (ny0>=height() || ny2<0) return *this;
43747       float
43748         ptxl = (ntx1 - ntx0)/(ny1 - ny0),
43749         ptxr = (ntx2 - ntx0)/(ny2 - ny0),
43750         ptxn = (ntx2 - ntx1)/(ny2 - ny1),
43751         ptyl = (nty1 - nty0)/(ny1 - ny0),
43752         ptyr = (nty2 - nty0)/(ny2 - ny0),
43753         ptyn = (nty2 - nty1)/(ny2 - ny1),
43754         txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
43755         tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
43756         txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):
43757           (ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
43758         tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):
43759           (ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
43760       tzfloat
43761         pzl = (nz1 - nz0)/(ny1 - ny0),
43762         pzr = (nz2 - nz0)/(ny2 - ny0),
43763         pzn = (nz2 - nz1)/(ny2 - ny1),
43764         zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
43765         zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
43766       const bool is_bump = texture._spectrum>=_spectrum + 2;
43767       const ulongT obx = twh*_spectrum, oby = twh*(_spectrum + 1);
43768 
43769       _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
43770                           nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
43771         if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
43772         int
43773           xleft = xleft0, xright = xright0,
43774           lxleft = lxleft0, lxright = lxright0,
43775           lyleft = lyleft0, lyright = lyright0;
43776         float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
43777         tzfloat zleft = zl, zright = zr;
43778         if (xright<xleft)
43779           cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
43780         const int
43781           dx = xright - xleft,
43782           dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
43783           dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
43784           rlx = dx?(lxright - lxleft)/dx:0,
43785           rly = dx?(lyright - lyleft)/dx:0,
43786           slx = lxright>lxleft?1:-1,
43787           sly = lyright>lyleft?1:-1,
43788           ndlx = dlx - (dx?dx*(dlx/dx):0),
43789           ndly = dly - (dx?dx*(dly/dx):0);
43790         float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
43791         const tzfloat pentez = (zright - zleft)/dx;
43792         int errlx = dx>>1, errly = errlx;
43793         if (xleft<0 && dx) {
43794           zleft-=xleft*(zright - zleft)/dx;
43795           lxleft-=xleft*(lxright - lxleft)/dx;
43796           lyleft-=xleft*(lyright - lyleft)/dx;
43797           txleft-=xleft*(txright - txleft)/dx;
43798           tyleft-=xleft*(tyright - tyleft)/dx;
43799         }
43800         if (xleft<0) xleft = 0;
43801         if (xright>=width() - 1) xright = width() - 1;
43802         T* ptrd = data(xleft,y);
43803         tz *ptrz = zbuffer.data(xleft,y);
43804         if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
43805             if (zleft>=(tzfloat)*ptrz) {
43806               *ptrz = (tz)zleft;
43807               const tzfloat invz = 1/zleft;
43808               const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
43809               const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
43810               const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
43811               cimg_forC(*this,c) {
43812                 const tl l = *lig;
43813                 *ptrd = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
43814                 ptrd+=whd; col+=twh; lig+=lwh;
43815               }
43816               ptrd-=offx;
43817             }
43818             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
43819             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
43820             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
43821           } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
43822             if (zleft>=(tzfloat)*ptrz) {
43823               *ptrz = (tz)zleft;
43824               const tzfloat invz = 1/zleft;
43825               const tc *col = &texture._atXY((int)(txleft*invz),(int)(tyleft*invz));
43826               const int bx = is_bump?128 - (int)col[obx]:0, by = is_bump?128 - (int)col[oby]:0;
43827               const tl *lig = &light._atXY(lxleft + bx,lyleft + by);
43828               cimg_forC(*this,c) {
43829                 const tl l = *lig;
43830                 const T val = (T)(l<1?l**col:(2 - l)**col + (l - 1)*maxval);
43831                 *ptrd = (T)(nopacity*val + *ptrd*copacity);
43832                 ptrd+=whd; col+=twh; lig+=lwh;
43833               }
43834               ptrd-=offx;
43835             }
43836             zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
43837             lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
43838             lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
43839           }
43840         zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
43841       }
43842       return *this;
43843     }
43844 
43845     //! Draw a filled 4d rectangle.
43846     /**
43847        \param x0 X-coordinate of the upper-left rectangle corner.
43848        \param y0 Y-coordinate of the upper-left rectangle corner.
43849        \param z0 Z-coordinate of the upper-left rectangle corner.
43850        \param c0 C-coordinate of the upper-left rectangle corner.
43851        \param x1 X-coordinate of the lower-right rectangle corner.
43852        \param y1 Y-coordinate of the lower-right rectangle corner.
43853        \param z1 Z-coordinate of the lower-right rectangle corner.
43854        \param c1 C-coordinate of the lower-right rectangle corner.
43855        \param val Scalar value used to fill the rectangle area.
43856        \param opacity Drawing opacity.
43857     **/
43858     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
43859                             const int x1, const int y1, const int z1, const int c1,
43860                             const T val, const float opacity=1) {
43861       if (is_empty()) return *this;
43862       const int
43863         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
43864         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
43865         nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
43866         nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
43867       const int
43868         lX = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
43869         lY = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
43870         lZ = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
43871         lC = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
43872       const ulongT
43873         offX = (ulongT)_width - lX,
43874         offY = (ulongT)_width*(_height - lY),
43875         offZ = (ulongT)_width*_height*(_depth - lZ);
43876       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
43877       T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
43878       if (lX>0 && lY>0 && lZ>0 && lC>0)
43879         for (int v = 0; v<lC; ++v) {
43880           for (int z = 0; z<lZ; ++z) {
43881             for (int y = 0; y<lY; ++y) {
43882               if (opacity>=1) {
43883                 if (sizeof(T)!=1) { for (int x = 0; x<lX; ++x) *(ptrd++) = val; ptrd+=offX; }
43884                 else { std::memset(ptrd,(int)val,lX); ptrd+=_width; }
43885               } else { for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
43886             }
43887             ptrd+=offY;
43888           }
43889           ptrd+=offZ;
43890         }
43891       return *this;
43892     }
43893 
43894     //! Draw a filled 3d rectangle.
43895     /**
43896        \param x0 X-coordinate of the upper-left rectangle corner.
43897        \param y0 Y-coordinate of the upper-left rectangle corner.
43898        \param z0 Z-coordinate of the upper-left rectangle corner.
43899        \param x1 X-coordinate of the lower-right rectangle corner.
43900        \param y1 Y-coordinate of the lower-right rectangle corner.
43901        \param z1 Z-coordinate of the lower-right rectangle corner.
43902        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
43903        \param opacity Drawing opacity.
43904     **/
43905     template<typename tc>
43906     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
43907                             const int x1, const int y1, const int z1,
43908                             const tc *const color, const float opacity=1) {
43909       if (is_empty()) return *this;
43910       if (!color)
43911         throw CImgArgumentException(_cimg_instance
43912                                     "draw_rectangle(): Specified color is (null).",
43913                                     cimg_instance);
43914       cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
43915       return *this;
43916     }
43917 
43918     //! Draw an outlined 3d rectangle \overloading.
43919     template<typename tc>
43920     CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
43921                             const int x1, const int y1, const int z1,
43922                             const tc *const color, const float opacity,
43923                             const unsigned int pattern) {
43924       return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true).
43925 	draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false).
43926 	draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false).
43927 	draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false).
43928 	draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true).
43929 	draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false).
43930 	draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false).
43931 	draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false).
43932 	draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true).
43933 	draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true).
43934 	draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true).
43935 	draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true);
43936     }
43937 
43938     //! Draw a filled 2d rectangle.
43939     /**
43940        \param x0 X-coordinate of the upper-left rectangle corner.
43941        \param y0 Y-coordinate of the upper-left rectangle corner.
43942        \param x1 X-coordinate of the lower-right rectangle corner.
43943        \param y1 Y-coordinate of the lower-right rectangle corner.
43944        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
43945        \param opacity Drawing opacity.
43946     **/
43947     template<typename tc>
43948     CImg<T>& draw_rectangle(const int x0, const int y0,
43949                             const int x1, const int y1,
43950                             const tc *const color, const float opacity=1) {
43951       return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity);
43952     }
43953 
43954     //! Draw a outlined 2d rectangle \overloading.
43955     template<typename tc>
43956     CImg<T>& draw_rectangle(const int x0, const int y0,
43957                             const int x1, const int y1,
43958                             const tc *const color, const float opacity,
43959                             const unsigned int pattern) {
43960       if (is_empty()) return *this;
43961       if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
43962       if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
43963       const int
43964         nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
43965         ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0;
43966       if (ny1==ny0 + 1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
43967                       draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
43968       return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
43969         draw_line(nx1,ny0 + 1,nx1,ny1 - 1,color,opacity,pattern,false).
43970         draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
43971         draw_line(nx0,ny1 - 1,nx0,ny0 + 1,color,opacity,pattern,false);
43972     }
43973 
43974     //! Draw a filled 2d polygon.
43975     /**
43976        \param points Set of polygon vertices.
43977        \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
43978        \param opacity Drawing opacity.
43979      **/
43980     template<typename tp, typename tc>
43981     CImg<T>& draw_polygon(const CImg<tp>& points,
43982                           const tc *const color, const float opacity=1) {
43983       if (is_empty() || !points) return *this;
43984       if (!color)
43985         throw CImgArgumentException(_cimg_instance
43986                                     "draw_polygon(): Specified color is (null).",
43987                                     cimg_instance);
43988       if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity);
43989       if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1),
43990                                              (int)points(1,0),(int)points(1,1),color,opacity);
43991       if (points._width==3) return draw_triangle((int)points(0,0),(int)points(0,1),
43992                                                  (int)points(1,0),(int)points(1,1),
43993                                                  (int)points(2,0),(int)points(2,1),color,opacity);
43994       cimg_init_scanline(color,opacity);
43995       int
43996         xmin = 0, ymin = 0,
43997         xmax = points.get_shared_row(0).max_min(xmin),
43998         ymax = points.get_shared_row(1).max_min(ymin);
43999       if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
44000       if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity);
44001 
44002       ymin = std::max(0,ymin);
44003       ymax = std::min(height() - 1,ymax);
44004       CImg<intT> Xs(points._width,ymax - ymin + 1);
44005       CImg<uintT> count(Xs._height,1,1,1,0);
44006       unsigned int n = 0, nn = 1;
44007       bool go_on = true;
44008 
44009       while (go_on) {
44010         unsigned int an = (nn + 1)%points._width;
44011         const int
44012           x0 = (int)points(n,0),
44013           y0 = (int)points(n,1);
44014         if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; }
44015         const int
44016           x1 = (int)points(nn,0),
44017           y1 = (int)points(nn,1);
44018         unsigned int tn = an;
44019         while (points(tn,1)==y1) (tn+=1)%=points._width;
44020 
44021         if (y0!=y1) {
44022           const int
44023             y2 = (int)points(tn,1),
44024             x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1,
44025             dy = cimg::sign(y01),
44026             tmax = std::max(1,cimg::abs(y01)),
44027             tend = tmax - (dy==cimg::sign(y12));
44028           unsigned int y = (unsigned int)y0 - ymin;
44029           for (int t = 0; t<=tend; ++t, y+=dy)
44030             if (y<Xs._height) Xs(count[y]++,y) = x0 + t*x01/tmax;
44031         }
44032 
44033         go_on = nn>n;
44034         n = nn;
44035         nn = an;
44036       }
44037 
44038       cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>32))
44039       cimg_forY(Xs,y) {
44040         const CImg<intT> Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort();
44041         int px = width();
44042         for (unsigned int n = 0; n<Xsy._width; n+=2) {
44043           int x0 = Xsy[n];
44044           const int x1 = Xsy[n + 1];
44045           x0+=x0==px;
44046           cimg_draw_scanline(x0,x1,y + ymin,color,opacity,1);
44047           px = x1;
44048         }
44049       }
44050       return *this;
44051     }
44052 
44053     //! Draw a outlined 2d polygon \overloading.
44054     template<typename t, typename tc>
44055     CImg<T>& draw_polygon(const CImg<t>& points,
44056                           const tc *const color, const float opacity, const unsigned int pattern) {
44057       if (is_empty() || !points || points._width<3) return *this;
44058       bool ninit_hatch = true;
44059       switch (points._height) {
44060       case 0 : case 1 :
44061         throw CImgArgumentException(_cimg_instance
44062                                     "draw_polygon(): Invalid specified point set.",
44063                                     cimg_instance);
44064       case 2 : { // 2d version.
44065         CImg<intT> npoints(points._width,2);
44066         int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
44067         unsigned int nb_points = 1;
44068         for (unsigned int p = 1; p<points._width; ++p) {
44069           const int nx = (int)points(p,0), ny = (int)points(p,1);
44070           if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
44071         }
44072         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
44073         int ox = x0, oy = y0;
44074         for (unsigned int i = 1; i<nb_points; ++i) {
44075           const int x = (int)npoints(i,0), y = (int)npoints(i,1);
44076           draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
44077           ninit_hatch = false;
44078           ox = x; oy = y;
44079         }
44080         draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
44081       } break;
44082       default : { // 3d version.
44083         CImg<intT> npoints(points._width,3);
44084         int
44085           x = npoints(0,0) = (int)points(0,0),
44086           y = npoints(0,1) = (int)points(0,1),
44087           z = npoints(0,2) = (int)points(0,2);
44088         unsigned int nb_points = 1;
44089         for (unsigned int p = 1; p<points._width; ++p) {
44090           const int nx = (int)points(p,0), ny = (int)points(p,1), nz = (int)points(p,2);
44091           if (nx!=x || ny!=y || nz!=z) {
44092             npoints(nb_points,0) = nx; npoints(nb_points,1) = ny; npoints(nb_points++,2) = nz;
44093             x = nx; y = ny; z = nz;
44094           }
44095         }
44096         const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1), z0 = (int)npoints(0,2);
44097         int ox = x0, oy = y0, oz = z0;
44098         for (unsigned int i = 1; i<nb_points; ++i) {
44099           const int x = (int)npoints(i,0), y = (int)npoints(i,1), z = (int)npoints(i,2);
44100           draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
44101           ninit_hatch = false;
44102           ox = x; oy = y; oz = z;
44103         }
44104         draw_line(ox,oy,oz,x0,y0,z0,color,opacity,pattern,false);
44105       }
44106       }
44107       return *this;
44108     }
44109 
44110     //! Draw a filled 2d ellipse.
44111     /**
44112        \param x0 X-coordinate of the ellipse center.
44113        \param y0 Y-coordinate of the ellipse center.
44114        \param r1 First radius of the ellipse.
44115        \param r2 Second radius of the ellipse.
44116        \param angle Angle of the first radius.
44117        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44118        \param opacity Drawing opacity.
44119     **/
44120     template<typename tc>
44121     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
44122                           const tc *const color, const float opacity=1) {
44123       return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U);
44124     }
44125 
44126     //! Draw a filled 2d ellipse \overloading.
44127     /**
44128        \param x0 X-coordinate of the ellipse center.
44129        \param y0 Y-coordinate of the ellipse center.
44130        \param tensor Diffusion tensor describing the ellipse.
44131        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44132        \param opacity Drawing opacity.
44133     **/
44134     template<typename t, typename tc>
44135     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
44136                           const tc *const color, const float opacity=1) {
44137       CImgList<t> eig = tensor.get_symmetric_eigen();
44138       const CImg<t> &val = eig[0], &vec = eig[1];
44139       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
44140                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
44141                           color,opacity);
44142     }
44143 
44144     //! Draw an outlined 2d ellipse.
44145     /**
44146        \param x0 X-coordinate of the ellipse center.
44147        \param y0 Y-coordinate of the ellipse center.
44148        \param r1 First radius of the ellipse.
44149        \param r2 Second radius of the ellipse.
44150        \param angle Angle of the first radius.
44151        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44152        \param opacity Drawing opacity.
44153        \param pattern An integer whose bits describe the outline pattern.
44154     **/
44155     template<typename tc>
44156     CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
44157                           const tc *const color, const float opacity, const unsigned int pattern) {
44158       if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern);
44159       return *this;
44160     }
44161 
44162     //! Draw an outlined 2d ellipse \overloading.
44163     /**
44164        \param x0 X-coordinate of the ellipse center.
44165        \param y0 Y-coordinate of the ellipse center.
44166        \param tensor Diffusion tensor describing the ellipse.
44167        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44168        \param opacity Drawing opacity.
44169        \param pattern An integer whose bits describe the outline pattern.
44170     **/
44171     template<typename t, typename tc>
44172     CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
44173                           const tc *const color, const float opacity,
44174                           const unsigned int pattern) {
44175       CImgList<t> eig = tensor.get_symmetric_eigen();
44176       const CImg<t> &val = eig[0], &vec = eig[1];
44177       return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
44178                           std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
44179                           color,opacity,pattern);
44180     }
44181 
44182     template<typename tc>
44183     CImg<T>& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
44184                            const tc *const color, const float opacity,
44185                            const unsigned int pattern) {
44186       if (is_empty()) return *this;
44187       if (!color)
44188         throw CImgArgumentException(_cimg_instance
44189                                     "draw_ellipse(): Specified color is (null).",
44190                                     cimg_instance);
44191       if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity);
44192       if (r1==r2 && (float)(int)r1==r1) {
44193         if (pattern) return draw_circle(x0,y0,(int)cimg::round(r1),color,opacity,pattern);
44194         else return draw_circle(x0,y0,(int)cimg::round(r1),color,opacity);
44195       }
44196       cimg_init_scanline(color,opacity);
44197       const float
44198         nr1 = cimg::abs(r1) - 0.5, nr2 = cimg::abs(r2) - 0.5,
44199         nangle = (float)(angle*cimg::PI/180),
44200         u = (float)std::cos(nangle),
44201         v = (float)std::sin(nangle),
44202         rmax = std::max(nr1,nr2),
44203         l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2),
44204         l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2),
44205         a = l1*u*u + l2*v*v,
44206         b = u*v*(l1 - l2),
44207         c = l1*v*v + l2*u*u;
44208       const int
44209         yb = (int)cimg::round(std::sqrt(a*rmax*rmax/(a*c - b*b))),
44210         tymin = y0 - yb - 1,
44211         tymax = y0 + yb + 1,
44212         ymin = tymin<0?0:tymin,
44213         ymax = tymax>=height()?height() - 1:tymax;
44214       int oxmin = 0, oxmax = 0;
44215       bool first_line = true;
44216       for (int y = ymin; y<=ymax; ++y) {
44217         const float
44218           Y = y - y0 + (y<y0?0.5f:-0.5f),
44219           delta = b*b*Y*Y - a*(c*Y*Y - rmax*rmax),
44220           sdelta = delta>0?(float)std::sqrt(delta)/a:0.0f,
44221           bY = b*Y/a,
44222           fxmin = x0 - 0.5f - bY - sdelta,
44223           fxmax = x0 + 0.5f - bY + sdelta;
44224         const int xmin = (int)cimg::round(fxmin), xmax = (int)cimg::round(fxmax);
44225         if (!pattern) cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
44226         else {
44227           if (first_line) {
44228             if (y0 - yb>=0) cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
44229             else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity);
44230             first_line = false;
44231           } else {
44232             if (xmin<oxmin) cimg_draw_scanline(xmin,oxmin - 1,y,color,opacity,1);
44233             else cimg_draw_scanline(oxmin + (oxmin==xmin?0:1),xmin,y,color,opacity,1);
44234             if (xmax<oxmax) cimg_draw_scanline(xmax,oxmax - 1,y,color,opacity,1);
44235             else cimg_draw_scanline(oxmax + (oxmax==xmax?0:1),xmax,y,color,opacity,1);
44236             if (y==tymax) cimg_draw_scanline(xmin + 1,xmax - 1,y,color,opacity,1);
44237           }
44238         }
44239         oxmin = xmin; oxmax = xmax;
44240       }
44241       return *this;
44242     }
44243 
44244     //! Draw a filled 2d circle.
44245     /**
44246        \param x0 X-coordinate of the circle center.
44247        \param y0 Y-coordinate of the circle center.
44248        \param radius  Circle radius.
44249        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44250        \param opacity Drawing opacity.
44251        \note
44252        - Circle version of the Bresenham's algorithm is used.
44253     **/
44254     template<typename tc>
44255     CImg<T>& draw_circle(const int x0, const int y0, int radius,
44256                          const tc *const color, const float opacity=1) {
44257       if (is_empty()) return *this;
44258       if (!color)
44259         throw CImgArgumentException(_cimg_instance
44260                                     "draw_circle(): Specified color is (null).",
44261                                     cimg_instance);
44262       cimg_init_scanline(color,opacity);
44263       if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
44264       if (y0>=0 && y0<height()) cimg_draw_scanline(x0 - radius,x0 + radius,y0,color,opacity,1);
44265       for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
44266         if (f>=0) {
44267           const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y;
44268           if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
44269           if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
44270           f+=(ddFy+=2); --y;
44271         }
44272         const bool no_diag = y!=(x++);
44273         ++(f+=(ddFx+=2));
44274         const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x;
44275         if (no_diag) {
44276           if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
44277           if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
44278         }
44279       }
44280       return *this;
44281     }
44282 
44283     //! Draw an outlined 2d circle.
44284     /**
44285        \param x0 X-coordinate of the circle center.
44286        \param y0 Y-coordinate of the circle center.
44287        \param radius Circle radius.
44288        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44289        \param opacity Drawing opacity.
44290        \param pattern An integer whose bits describe the outline pattern.
44291     **/
44292     template<typename tc>
44293     CImg<T>& draw_circle(const int x0, const int y0, int radius,
44294                          const tc *const color, const float opacity,
44295                          const unsigned int pattern) {
44296       cimg::unused(pattern);
44297       if (is_empty()) return *this;
44298       if (!color)
44299         throw CImgArgumentException(_cimg_instance
44300                                     "draw_circle(): Specified color is (null).",
44301                                     cimg_instance);
44302       if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
44303       if (!radius) return draw_point(x0,y0,color,opacity);
44304       draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity).
44305         draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity);
44306       if (radius==1) return *this;
44307       for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
44308         if (f>=0) { f+=(ddFy+=2); --y; }
44309         ++x; ++(f+=(ddFx+=2));
44310         if (x!=y + 1) {
44311           const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x,
44312             x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y;
44313           draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
44314             draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
44315           if (x!=y)
44316             draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
44317               draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
44318         }
44319       }
44320       return *this;
44321     }
44322 
44323     //! Draw an image.
44324     /**
44325        \param sprite Sprite image.
44326        \param x0 X-coordinate of the sprite position.
44327        \param y0 Y-coordinate of the sprite position.
44328        \param z0 Z-coordinate of the sprite position.
44329        \param c0 C-coordinate of the sprite position.
44330        \param opacity Drawing opacity.
44331     **/
44332     template<typename t>
44333     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
44334                         const CImg<t>& sprite, const float opacity=1) {
44335       if (is_empty() || !sprite) return *this;
44336       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
44337       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
44338         return assign(sprite,false);
44339       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
44340       const int
44341         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
44342         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
44343         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
44344         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
44345       const t
44346         *ptrs = sprite._data +
44347         (bx?-x0:0) +
44348         (by?-y0*(ulongT)sprite.width():0) +
44349         (bz?-z0*(ulongT)sprite.width()*sprite.height():0) +
44350         (bc?-c0*(ulongT)sprite.width()*sprite.height()*sprite.depth():0);
44351       const ulongT
44352         offX = (ulongT)_width - lX,
44353         soffX = (ulongT)sprite._width - lX,
44354         offY = (ulongT)_width*(_height - lY),
44355         soffY = (ulongT)sprite._width*(sprite._height - lY),
44356         offZ = (ulongT)_width*_height*(_depth - lZ),
44357         soffZ = (ulongT)sprite._width*sprite._height*(sprite._depth - lZ);
44358       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
44359       if (lX>0 && lY>0 && lZ>0 && lC>0) {
44360         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
44361         for (int v = 0; v<lC; ++v) {
44362           for (int z = 0; z<lZ; ++z) {
44363             for (int y = 0; y<lY; ++y) {
44364               if (opacity>=1) for (int x = 0; x<lX; ++x) *(ptrd++) = (T)*(ptrs++);
44365               else for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
44366               ptrd+=offX; ptrs+=soffX;
44367             }
44368             ptrd+=offY; ptrs+=soffY;
44369           }
44370           ptrd+=offZ; ptrs+=soffZ;
44371         }
44372       }
44373       return *this;
44374     }
44375 
44376     //! Draw an image \specialization.
44377     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
44378                         const CImg<T>& sprite, const float opacity=1) {
44379       if (is_empty() || !sprite) return *this;
44380       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
44381       if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
44382         return assign(sprite,false);
44383       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
44384       const int
44385         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
44386         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
44387         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
44388         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
44389       const T
44390         *ptrs = sprite._data +
44391         (bx?-x0:0) +
44392         (by?-y0*(ulongT)sprite.width():0) +
44393         (bz?-z0*(ulongT)sprite.width()*sprite.height():0) +
44394         (bc?-c0*(ulongT)sprite.width()*sprite.height()*sprite.depth():0);
44395       const ulongT
44396         offX = (ulongT)_width - lX,
44397         soffX = (ulongT)sprite._width - lX,
44398         offY = (ulongT)_width*(_height - lY),
44399         soffY = (ulongT)sprite._width*(sprite._height - lY),
44400         offZ = (ulongT)_width*_height*(_depth - lZ),
44401         soffZ = (ulongT)sprite._width*sprite._height*(sprite._depth - lZ),
44402         slX = lX*sizeof(T);
44403       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
44404       if (lX>0 && lY>0 && lZ>0 && lC>0) {
44405         T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
44406         for (int v = 0; v<lC; ++v) {
44407           for (int z = 0; z<lZ; ++z) {
44408             if (opacity>=1)
44409               for (int y = 0; y<lY; ++y) { std::memcpy(ptrd,ptrs,slX); ptrd+=_width; ptrs+=sprite._width; }
44410             else for (int y = 0; y<lY; ++y) {
44411                 for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
44412                 ptrd+=offX; ptrs+=soffX;
44413               }
44414             ptrd+=offY; ptrs+=soffY;
44415           }
44416           ptrd+=offZ; ptrs+=soffZ;
44417         }
44418       }
44419       return *this;
44420     }
44421 
44422     //! Draw an image \overloading.
44423     template<typename t>
44424     CImg<T>& draw_image(const int x0, const int y0, const int z0,
44425                         const CImg<t>& sprite, const float opacity=1) {
44426       return draw_image(x0,y0,z0,0,sprite,opacity);
44427     }
44428 
44429     //! Draw an image \overloading.
44430     template<typename t>
44431     CImg<T>& draw_image(const int x0, const int y0,
44432                         const CImg<t>& sprite, const float opacity=1) {
44433       return draw_image(x0,y0,0,sprite,opacity);
44434     }
44435 
44436     //! Draw an image \overloading.
44437     template<typename t>
44438     CImg<T>& draw_image(const int x0,
44439                         const CImg<t>& sprite, const float opacity=1) {
44440       return draw_image(x0,0,sprite,opacity);
44441     }
44442 
44443     //! Draw an image \overloading.
44444     template<typename t>
44445     CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
44446       return draw_image(0,sprite,opacity);
44447     }
44448 
44449     //! Draw a masked image.
44450     /**
44451        \param sprite Sprite image.
44452        \param mask Mask image.
44453        \param x0 X-coordinate of the sprite position in the image instance.
44454        \param y0 Y-coordinate of the sprite position in the image instance.
44455        \param z0 Z-coordinate of the sprite position in the image instance.
44456        \param c0 C-coordinate of the sprite position in the image instance.
44457        \param mask_max_value Maximum pixel value of the mask image \c mask.
44458        \param opacity Drawing opacity.
44459        \note
44460        - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
44461        - Dimensions along x,y and z of \p sprite and \p mask must be the same.
44462     **/
44463     template<typename ti, typename tm>
44464     CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
44465                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
44466                         const float mask_max_value=1) {
44467       if (is_empty() || !sprite || !mask) return *this;
44468       if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
44469       if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
44470       if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
44471         throw CImgArgumentException(_cimg_instance
44472                                     "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have "
44473                                     "incompatible dimensions.",
44474                                     cimg_instance,
44475                                     sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
44476                                     mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
44477 
44478       const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
44479       const int
44480         lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
44481         lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
44482         lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
44483         lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
44484       const ulongT
44485         coff = (bx?-x0:0) +
44486         (by?-y0*(ulongT)mask.width():0) +
44487         (bz?-z0*(ulongT)mask.width()*mask.height():0) +
44488         (bc?-c0*(ulongT)mask.width()*mask.height()*mask.depth():0),
44489         ssize = (ulongT)mask.width()*mask.height()*mask.depth()*mask.spectrum();
44490       const ti *ptrs = sprite._data + coff;
44491       const tm *ptrm = mask._data + coff;
44492       const ulongT
44493         offX = (ulongT)_width - lX,
44494         soffX = (ulongT)sprite._width - lX,
44495         offY = (ulongT)_width*(_height - lY),
44496         soffY = (ulongT)sprite._width*(sprite._height - lY),
44497         offZ = (ulongT)_width*_height*(_depth - lZ),
44498         soffZ = (ulongT)sprite._width*sprite._height*(sprite._depth - lZ);
44499       if (lX>0 && lY>0 && lZ>0 && lC>0) {
44500 	T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
44501         for (int c = 0; c<lC; ++c) {
44502           ptrm = mask._data + (ptrm - mask._data)%ssize;
44503           for (int z = 0; z<lZ; ++z) {
44504             for (int y = 0; y<lY; ++y) {
44505               for (int x = 0; x<lX; ++x) {
44506                 const float mopacity = (float)(*(ptrm++)*opacity),
44507                   nopacity = cimg::abs(mopacity), copacity = mask_max_value - std::max(mopacity,0.0f);
44508                 *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
44509                 ++ptrd;
44510               }
44511               ptrd+=offX; ptrs+=soffX; ptrm+=soffX;
44512             }
44513             ptrd+=offY; ptrs+=soffY; ptrm+=soffY;
44514           }
44515           ptrd+=offZ; ptrs+=soffZ; ptrm+=soffZ;
44516         }
44517       }
44518       return *this;
44519     }
44520 
44521     //! Draw a masked image \overloading.
44522     template<typename ti, typename tm>
44523     CImg<T>& draw_image(const int x0, const int y0, const int z0,
44524                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
44525                         const float mask_max_value=1) {
44526       return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
44527     }
44528 
44529     //! Draw a image \overloading.
44530     template<typename ti, typename tm>
44531     CImg<T>& draw_image(const int x0, const int y0,
44532                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
44533                         const float mask_max_value=1) {
44534       return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
44535     }
44536 
44537     //! Draw a image \overloading.
44538     template<typename ti, typename tm>
44539     CImg<T>& draw_image(const int x0,
44540                         const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
44541                         const float mask_max_value=1) {
44542       return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
44543     }
44544 
44545     //! Draw an image.
44546     template<typename ti, typename tm>
44547     CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
44548                         const float mask_max_value=1) {
44549       return draw_image(0,sprite,mask,opacity,mask_max_value);
44550     }
44551 
44552     //! Draw a text string.
44553     /**
44554        \param x0 X-coordinate of the text in the image instance.
44555        \param y0 Y-coordinate of the text in the image instance.
44556        \param text Format of the text ('printf'-style format string).
44557        \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color.
44558        \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color.
44559        \param opacity Drawing opacity.
44560        \param font Font used for drawing text.
44561     **/
44562     template<typename tc1, typename tc2, typename t>
44563     CImg<T>& draw_text(const int x0, const int y0,
44564                        const char *const text,
44565                        const tc1 *const foreground_color, const tc2 *const background_color,
44566                        const float opacity, const CImgList<t>& font, ...) {
44567       if (!font) return *this;
44568       CImg<charT> tmp(2048);
44569       std::va_list ap; va_start(ap,font);
44570       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
44571       return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false);
44572     }
44573 
44574     //! Draw a text string \overloading.
44575     /**
44576        \note A transparent background is used for the text.
44577     **/
44578     template<typename tc, typename t>
44579     CImg<T>& draw_text(const int x0, const int y0,
44580                        const char *const text,
44581                        const tc *const foreground_color, const int,
44582                        const float opacity, const CImgList<t>& font, ...) {
44583       if (!font) return *this;
44584       CImg<charT> tmp(2048);
44585       std::va_list ap; va_start(ap,font);
44586       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
44587       return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false);
44588     }
44589 
44590     //! Draw a text string \overloading.
44591     /**
44592        \note A transparent foreground is used for the text.
44593     **/
44594     template<typename tc, typename t>
44595     CImg<T>& draw_text(const int x0, const int y0,
44596                        const char *const text,
44597                        const int, const tc *const background_color,
44598                        const float opacity, const CImgList<t>& font, ...) {
44599       if (!font) return *this;
44600       CImg<charT> tmp(2048);
44601       std::va_list ap; va_start(ap,font);
44602       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
44603       return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false);
44604     }
44605 
44606     //! Draw a text string \overloading.
44607     /**
44608        \param x0 X-coordinate of the text in the image instance.
44609        \param y0 Y-coordinate of the text in the image instance.
44610        \param text Format of the text ('printf'-style format string).
44611        \param foreground_color Array of spectrum() values of type \c T,
44612          defining the foreground color (0 means 'transparent').
44613        \param background_color Array of spectrum() values of type \c T,
44614          defining the background color (0 means 'transparent').
44615        \param opacity Drawing opacity.
44616        \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise).
44617     **/
44618     template<typename tc1, typename tc2>
44619     CImg<T>& draw_text(const int x0, const int y0,
44620                        const char *const text,
44621                        const tc1 *const foreground_color, const tc2 *const background_color,
44622                        const float opacity=1, const unsigned int font_height=13, ...) {
44623       if (!font_height) return *this;
44624       CImg<charT> tmp(2048);
44625       std::va_list ap; va_start(ap,font_height);
44626       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
44627       const CImgList<ucharT>& font = CImgList<ucharT>::font(font_height,true);
44628       _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true);
44629       return *this;
44630     }
44631 
44632     //! Draw a text string \overloading.
44633     template<typename tc>
44634     CImg<T>& draw_text(const int x0, const int y0,
44635                        const char *const text,
44636                        const tc *const foreground_color, const int background_color=0,
44637                        const float opacity=1, const unsigned int font_height=13, ...) {
44638       if (!font_height) return *this;
44639       cimg::unused(background_color);
44640       CImg<charT> tmp(2048);
44641       std::va_list ap; va_start(ap,font_height);
44642       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
44643       return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data);
44644     }
44645 
44646     //! Draw a text string \overloading.
44647     template<typename tc>
44648     CImg<T>& draw_text(const int x0, const int y0,
44649                        const char *const text,
44650                        const int, const tc *const background_color,
44651                        const float opacity=1, const unsigned int font_height=13, ...) {
44652       if (!font_height) return *this;
44653       CImg<charT> tmp(2048);
44654       std::va_list ap; va_start(ap,font_height);
44655       cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
44656       return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data);
44657     }
44658 
44659     template<typename tc1, typename tc2, typename t>
44660     CImg<T>& _draw_text(const int x0, const int y0,
44661                         const char *const text,
44662                         const tc1 *const foreground_color, const tc2 *const background_color,
44663                         const float opacity, const CImgList<t>& font,
44664                         const bool is_native_font) {
44665       if (!text) return *this;
44666       if (!font)
44667         throw CImgArgumentException(_cimg_instance
44668                                     "draw_text(): Empty specified font.",
44669                                     cimg_instance);
44670 
44671       const unsigned int text_length = (unsigned int)std::strlen(text);
44672       const bool _is_empty = is_empty();
44673       if (_is_empty) {
44674         // If needed, pre-compute necessary size of the image
44675         int x = 0, y = 0, w = 0;
44676         unsigned char c = 0;
44677         for (unsigned int i = 0; i<text_length; ++i) {
44678           c = (unsigned char)text[i];
44679           switch (c) {
44680           case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
44681           case '\t' : x+=4*font[' ']._width; break;
44682           default : if (c<font._width) x+=font[c]._width;
44683           }
44684         }
44685         if (x!=0 || c=='\n') {
44686           if (x>w) w=x;
44687           y+=font[0]._height;
44688         }
44689         assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0);
44690       }
44691 
44692       int x = x0, y = y0;
44693       for (unsigned int i = 0; i<text_length; ++i) {
44694         const unsigned char c = (unsigned char)text[i];
44695         switch (c) {
44696         case '\n' : y+=font[0]._height; x = x0; break;
44697         case '\t' : x+=4*font[' ']._width; break;
44698         default : if (c<font._width) {
44699             CImg<T> letter = font[c];
44700             if (letter) {
44701               if (is_native_font && _spectrum>letter._spectrum) letter.resize(-100,-100,1,_spectrum,0,2);
44702               const unsigned int cmin = std::min(_spectrum,letter._spectrum);
44703               if (foreground_color)
44704                 for (unsigned int c = 0; c<cmin; ++c)
44705                   if (foreground_color[c]!=1) letter.get_shared_channel(c)*=foreground_color[c];
44706               if (c + 256<font.width()) { // Letter has mask.
44707                 if (background_color)
44708                   for (unsigned int c = 0; c<cmin; ++c)
44709                     draw_rectangle(x,y,0,c,x + letter._width - 1,y + letter._height - 1,0,c,
44710                                    background_color[c],opacity);
44711                 draw_image(x,y,letter,font[c + 256],opacity,255.0f);
44712               } else draw_image(x,y,letter,opacity); // Letter has no mask.
44713               x+=letter._width;
44714             }
44715           }
44716         }
44717       }
44718       return *this;
44719     }
44720 
44721     //! Draw a 2d vector field.
44722     /**
44723        \param flow Image of 2d vectors used as input data.
44724        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
44725        \param opacity Drawing opacity.
44726        \param sampling Length (in pixels) between each arrow.
44727        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
44728        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
44729        \param pattern Used pattern to draw lines.
44730        \note Clipping is supported.
44731     **/
44732     template<typename t1, typename t2>
44733     CImg<T>& draw_quiver(const CImg<t1>& flow,
44734                          const t2 *const color, const float opacity=1,
44735                          const unsigned int sampling=25, const float factor=-20,
44736                          const bool is_arrow=true, const unsigned int pattern=~0U) {
44737       return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
44738     }
44739 
44740     //! Draw a 2d vector field, using a field of colors.
44741     /**
44742        \param flow Image of 2d vectors used as input data.
44743        \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
44744        \param opacity Opacity of the drawing.
44745        \param sampling Length (in pixels) between each arrow.
44746        \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
44747        \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
44748        \param pattern Used pattern to draw lines.
44749        \note Clipping is supported.
44750     **/
44751     template<typename t1, typename t2>
44752     CImg<T>& draw_quiver(const CImg<t1>& flow,
44753                          const CImg<t2>& color, const float opacity=1,
44754                          const unsigned int sampling=25, const float factor=-20,
44755                          const bool is_arrow=true, const unsigned int pattern=~0U) {
44756       if (is_empty()) return *this;
44757       if (!flow || flow._spectrum!=2)
44758         throw CImgArgumentException(_cimg_instance
44759                                     "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
44760                                     cimg_instance,
44761                                     flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
44762       if (sampling<=0)
44763         throw CImgArgumentException(_cimg_instance
44764                                     "draw_quiver(): Invalid sampling value %g "
44765                                     "(should be >0)",
44766                                     cimg_instance,
44767                                     sampling);
44768       const bool colorfield = (color._width==flow._width && color._height==flow._height &&
44769                                color._depth==1 && color._spectrum==_spectrum);
44770       if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
44771       float vmax,fact;
44772       if (factor<=0) {
44773         float m, M = (float)flow.get_norm(2).max_min(m);
44774         vmax = (float)std::max(cimg::abs(m),cimg::abs(M));
44775         if (!vmax) vmax = 1;
44776         fact = -factor;
44777       } else { fact = factor; vmax = 1; }
44778 
44779       for (unsigned int y = sampling/2; y<_height; y+=sampling)
44780         for (unsigned int x = sampling/2; x<_width; x+=sampling) {
44781           const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
44782           float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
44783           if (is_arrow) {
44784             const int xx = (int)(x + u), yy = (int)(y + v);
44785             if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.0f,pattern);
44786             else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.0f,pattern);
44787           } else {
44788             if (colorfield)
44789               draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
44790                         color.get_vector_at(X,Y)._data,opacity,pattern);
44791             else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
44792                            color._data,opacity,pattern);
44793           }
44794         }
44795       return *this;
44796     }
44797 
44798     //! Draw a labeled horizontal axis.
44799     /**
44800        \param values_x Values along the horizontal axis.
44801        \param y Y-coordinate of the horizontal axis in the image instance.
44802        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44803        \param opacity Drawing opacity.
44804        \param pattern Drawing pattern.
44805        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
44806        \param allow_zero Enable/disable the drawing of label '0' if found.
44807     **/
44808     template<typename t, typename tc>
44809     CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
44810                        const tc *const color, const float opacity=1,
44811                        const unsigned int pattern=~0U, const unsigned int font_height=13,
44812                        const bool allow_zero=true) {
44813       if (is_empty()) return *this;
44814       const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height;
44815       const int siz = (int)values_x.size() - 1;
44816       CImg<charT> txt(32);
44817       CImg<T> label;
44818       if (siz<=0) { // Degenerated case.
44819         draw_line(0,y,_width - 1,y,color,opacity,pattern);
44820         if (!siz) {
44821           cimg_snprintf(txt,txt._width,"%g",(double)*values_x);
44822           label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
44823           const int
44824             _xt = (width() - label.width())/2,
44825             xt = _xt<3?3:_xt + label.width()>=width() - 2?width() - 3 - label.width():_xt;
44826           draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity);
44827           if (allow_zero || *txt!='0' || txt[1]!=0)
44828             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
44829         }
44830       } else { // Regular case.
44831         if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width - 1,y,color,opacity,30,5,pattern);
44832         else draw_arrow(_width - 1,y,0,y,color,opacity,30,5,pattern);
44833         cimg_foroff(values_x,x) {
44834           cimg_snprintf(txt,txt._width,"%g",(double)values_x(x));
44835           label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
44836           const int
44837             xi = (int)(x*(_width - 1)/siz),
44838             _xt = xi - label.width()/2,
44839             xt = _xt<3?3:_xt + label.width()>=width() - 2?width() - 3 - label.width():_xt;
44840           draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity);
44841           if (allow_zero || *txt!='0' || txt[1]!=0)
44842             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
44843         }
44844       }
44845       return *this;
44846     }
44847 
44848     //! Draw a labeled vertical axis.
44849     /**
44850        \param x X-coordinate of the vertical axis in the image instance.
44851        \param values_y Values along the Y-axis.
44852        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44853        \param opacity Drawing opacity.
44854        \param pattern Drawing pattern.
44855        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
44856        \param allow_zero Enable/disable the drawing of label '0' if found.
44857     **/
44858     template<typename t, typename tc>
44859     CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
44860                        const tc *const color, const float opacity=1,
44861                        const unsigned int pattern=~0U, const unsigned int font_height=13,
44862                        const bool allow_zero=true) {
44863       if (is_empty()) return *this;
44864       int siz = (int)values_y.size() - 1;
44865       CImg<charT> txt(32);
44866       CImg<T> label;
44867       if (siz<=0) { // Degenerated case.
44868         draw_line(x,0,x,_height - 1,color,opacity,pattern);
44869         if (!siz) {
44870           cimg_snprintf(txt,txt._width,"%g",(double)*values_y);
44871           label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
44872           const int
44873             _yt = (height() - label.height())/2,
44874             yt = _yt<0?0:_yt + label.height()>=height()?height() - 1-label.height():_yt,
44875             _xt = x - 2 - label.width(),
44876             xt = _xt>=0?_xt:x + 3;
44877           draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity);
44878           if (allow_zero || *txt!='0' || txt[1]!=0)
44879             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
44880         }
44881       } else { // Regular case.
44882         if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height - 1,color,opacity,30,5,pattern);
44883         else draw_arrow(x,_height - 1,x,0,color,opacity,30,5,pattern);
44884         cimg_foroff(values_y,y) {
44885           cimg_snprintf(txt,txt._width,"%g",(double)values_y(y));
44886           label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
44887           const int
44888             yi = (int)(y*(_height - 1)/siz),
44889             _yt = yi - label.height()/2,
44890             yt = _yt<0?0:_yt + label.height()>=height()?height() - 1-label.height():_yt,
44891             _xt = x - 2 - label.width(),
44892             xt = _xt>=0?_xt:x + 3;
44893           draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity);
44894           if (allow_zero || *txt!='0' || txt[1]!=0)
44895             draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
44896         }
44897       }
44898       return *this;
44899     }
44900 
44901     //! Draw labeled horizontal and vertical axes.
44902     /**
44903        \param values_x Values along the X-axis.
44904        \param values_y Values along the Y-axis.
44905        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44906        \param opacity Drawing opacity.
44907        \param pattern_x Drawing pattern for the X-axis.
44908        \param pattern_y Drawing pattern for the Y-axis.
44909        \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
44910        \param allow_zero Enable/disable the drawing of label '0' if found.
44911     **/
44912     template<typename tx, typename ty, typename tc>
44913     CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
44914                        const tc *const color, const float opacity=1,
44915                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
44916                        const unsigned int font_height=13, const bool allow_zero=true) {
44917       if (is_empty()) return *this;
44918       const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
44919       const int sizx = (int)values_x.size() - 1, wm1 = width() - 1;
44920       if (sizx>=0) {
44921         float ox = (float)*nvalues_x;
44922         for (unsigned int x = sizx?1U:0U; x<_width; ++x) {
44923           const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
44924           if (nx*ox<=0) { draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero); break; }
44925           ox = nx;
44926         }
44927       }
44928       const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
44929       const int sizy = (int)values_y.size() - 1, hm1 = height() - 1;
44930       if (sizy>0) {
44931         float oy = (float)nvalues_y[0];
44932         for (unsigned int y = sizy?1U:0U; y<_height; ++y) {
44933           const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
44934           if (ny*oy<=0) { draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero); break; }
44935           oy = ny;
44936         }
44937       }
44938       return *this;
44939     }
44940 
44941     //! Draw labeled horizontal and vertical axes \overloading.
44942     template<typename tc>
44943     CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
44944                        const tc *const color, const float opacity=1,
44945                        const int subdivisionx=-60, const int subdivisiony=-60,
44946                        const float precisionx=0, const float precisiony=0,
44947                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
44948                        const unsigned int font_height=13) {
44949       if (is_empty()) return *this;
44950       const bool allow_zero = (x0*x1>0) || (y0*y1>0);
44951       const float
44952         dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0),
44953         px = dx<=0?1:precisionx==0?(float)std::pow(10.0,(int)std::log10(dx) - 2.0):precisionx,
44954         py = dy<=0?1:precisiony==0?(float)std::pow(10.0,(int)std::log10(dy) - 2.0):precisiony;
44955       if (x0!=x1 && y0!=y1)
44956         draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),
44957                   CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
44958                   color,opacity,pattern_x,pattern_y,font_height,allow_zero);
44959       else if (x0==x1 && y0!=y1)
44960         draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
44961                   color,opacity,pattern_y,font_height);
44962       else if (x0!=x1 && y0==y1)
44963         draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0,
44964                   color,opacity,pattern_x,font_height);
44965       return *this;
44966     }
44967 
44968     //! Draw 2d grid.
44969     /**
44970        \param values_x X-coordinates of the vertical lines.
44971        \param values_y Y-coordinates of the horizontal lines.
44972        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
44973        \param opacity Drawing opacity.
44974        \param pattern_x Drawing pattern for vertical lines.
44975        \param pattern_y Drawing pattern for horizontal lines.
44976     **/
44977     template<typename tx, typename ty, typename tc>
44978     CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
44979                        const tc *const color, const float opacity=1,
44980                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
44981       if (is_empty()) return *this;
44982       if (values_x) cimg_foroff(values_x,x) {
44983           const int xi = (int)values_x[x];
44984           if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height - 1,color,opacity,pattern_x);
44985         }
44986       if (values_y) cimg_foroff(values_y,y) {
44987           const int yi = (int)values_y[y];
44988           if (yi>=0 && yi<height()) draw_line(0,yi,_width - 1,yi,color,opacity,pattern_y);
44989         }
44990       return *this;
44991     }
44992 
44993     //! Draw 2d grid \simplification.
44994     template<typename tc>
44995     CImg<T>& draw_grid(const float delta_x,  const float delta_y,
44996                        const float offsetx, const float offsety,
44997                        const bool invertx, const bool inverty,
44998                        const tc *const color, const float opacity=1,
44999                        const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
45000       if (is_empty()) return *this;
45001       CImg<uintT> seqx, seqy;
45002       if (delta_x!=0) {
45003         const float dx = delta_x>0?delta_x:_width*-delta_x/100;
45004         const unsigned int nx = (unsigned int)(_width/dx);
45005         seqx = CImg<uintT>::sequence(1 + nx,0,(unsigned int)(dx*nx));
45006         if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width);
45007         if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
45008       }
45009       if (delta_y!=0) {
45010         const float dy = delta_y>0?delta_y:_height*-delta_y/100;
45011         const unsigned int ny = (unsigned int)(_height/dy);
45012         seqy = CImg<uintT>::sequence(1 + ny,0,(unsigned int)(dy*ny));
45013         if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height);
45014         if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
45015      }
45016       return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
45017     }
45018 
45019     //! Draw 1d graph.
45020     /**
45021        \param data Image containing the graph values I = f(x).
45022        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
45023        \param opacity Drawing opacity.
45024 
45025        \param plot_type Define the type of the plot:
45026                       - 0 = No plot.
45027                       - 1 = Plot using segments.
45028                       - 2 = Plot using cubic splines.
45029                       - 3 = Plot with bars.
45030        \param vertex_type Define the type of points:
45031                       - 0 = No points.
45032                       - 1 = Point.
45033                       - 2 = Straight cross.
45034                       - 3 = Diagonal cross.
45035                       - 4 = Filled circle.
45036                       - 5 = Outlined circle.
45037                       - 6 = Square.
45038                       - 7 = Diamond.
45039        \param ymin Lower bound of the y-range.
45040        \param ymax Upper bound of the y-range.
45041        \param pattern Drawing pattern.
45042        \note
45043          - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
45044     **/
45045     template<typename t, typename tc>
45046     CImg<T>& draw_graph(const CImg<t>& data,
45047                         const tc *const color, const float opacity=1,
45048                         const unsigned int plot_type=1, const int vertex_type=1,
45049                         const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
45050       if (is_empty() || _height<=1) return *this;
45051       if (!color)
45052         throw CImgArgumentException(_cimg_instance
45053                                     "draw_graph(): Specified color is (null).",
45054                                     cimg_instance);
45055 
45056       // Create shaded colors for displaying bar plots.
45057       CImg<tc> color1, color2;
45058       if (plot_type==3) {
45059         color1.assign(_spectrum); color2.assign(_spectrum);
45060         cimg_forC(*this,c) {
45061           color1[c] = (tc)std::min((float)cimg::type<tc>::max(),(float)color[c]*1.2f);
45062           color2[c] = (tc)(color[c]*0.4f);
45063         }
45064       }
45065 
45066       // Compute min/max and normalization factors.
45067       const ulongT
45068         siz = data.size(),
45069         _siz1 = siz - (plot_type!=3),
45070         siz1 = _siz1?_siz1:1;
45071       const unsigned int
45072         _width1 = _width - (plot_type!=3),
45073         width1 = _width1?_width1:1;
45074       double m = ymin, M = ymax;
45075       if (ymin==ymax) m = (double)data.max_min(M);
45076       if (m==M) { --m; ++M; }
45077       const float ca = (float)(M-m)/(_height - 1);
45078       bool init_hatch = true;
45079 
45080       // Draw graph edges
45081       switch (plot_type%4) {
45082       case 1 : { // Segments
45083         int oX = 0, oY = (int)((data[0] - m)/ca);
45084         if (siz==1) {
45085           const int Y = (int)((*data - m)/ca);
45086           draw_line(0,Y,width() - 1,Y,color,opacity,pattern);
45087         } else {
45088           const float fx = (float)_width/siz1;
45089           for (ulongT off = 1; off<siz; ++off) {
45090             const int
45091               X = (int)(off*fx) - 1,
45092               Y = (int)((data[off]-m)/ca);
45093             draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
45094             oX = X; oY = Y;
45095             init_hatch = false;
45096           }
45097         }
45098       } break;
45099       case 2 : { // Spline
45100         const CImg<t> ndata(data._data,siz,1,1,1,true);
45101         int oY = (int)((data[0] - m)/ca);
45102         cimg_forX(*this,x) {
45103           const int Y = (int)((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
45104           if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch);
45105           init_hatch = false;
45106           oY = Y;
45107         }
45108       } break;
45109       case 3 : { // Bars
45110         const int Y0 = (int)(-m/ca);
45111         const float fx = (float)_width/siz1;
45112         int oX = 0;
45113         cimg_foroff(data,off) {
45114           const int
45115             X = (int)((off + 1)*fx) - 1,
45116             Y = (int)((data[off] - m)/ca);
45117           draw_rectangle(oX,Y0,X,Y,color,opacity).
45118             draw_line(oX,Y,oX,Y0,color2.data(),opacity).
45119             draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
45120             draw_line(X,Y,X,Y0,color1.data(),opacity).
45121             draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
45122           oX = X + 1;
45123         }
45124       } break;
45125       default : break; // No edges
45126       }
45127 
45128       // Draw graph points
45129       const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
45130       const float fx = (float)_width1/siz1;
45131       switch (vertex_type%8) {
45132       case 1 : { // Point
45133         cimg_foroff(data,off) {
45134           const int
45135             X = (int)(off*fx + wb2),
45136             Y = (int)((data[off]-m)/ca);
45137           draw_point(X,Y,color,opacity);
45138         }
45139       } break;
45140       case 2 : { // Straight Cross
45141         cimg_foroff(data,off) {
45142           const int
45143             X = (int)(off*fx + wb2),
45144             Y = (int)((data[off]-m)/ca);
45145           draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity);
45146         }
45147       } break;
45148       case 3 : { // Diagonal Cross
45149         cimg_foroff(data,off) {
45150           const int
45151             X = (int)(off*fx + wb2),
45152             Y = (int)((data[off]-m)/ca);
45153           draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity);
45154         }
45155       } break;
45156       case 4 : { // Filled Circle
45157         cimg_foroff(data,off) {
45158           const int
45159             X = (int)(off*fx + wb2),
45160             Y = (int)((data[off]-m)/ca);
45161           draw_circle(X,Y,3,color,opacity);
45162         }
45163       } break;
45164       case 5 : { // Outlined circle
45165         cimg_foroff(data,off) {
45166           const int
45167             X = (int)(off*fx + wb2),
45168             Y = (int)((data[off]-m)/ca);
45169           draw_circle(X,Y,3,color,opacity,0U);
45170         }
45171       } break;
45172       case 6 : { // Square
45173         cimg_foroff(data,off) {
45174           const int
45175             X = (int)(off*fx + wb2),
45176             Y = (int)((data[off]-m)/ca);
45177           draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U);
45178         }
45179       } break;
45180       case 7 : { // Diamond
45181         cimg_foroff(data,off) {
45182           const int
45183             X = (int)(off*fx + wb2),
45184             Y = (int)((data[off]-m)/ca);
45185           draw_line(X,Y - 4,X + 4,Y,color,opacity).
45186             draw_line(X + 4,Y,X,Y + 4,color,opacity).
45187             draw_line(X,Y + 4,X - 4,Y,color,opacity).
45188             draw_line(X - 4,Y,X,Y - 4,color,opacity);
45189         }
45190       } break;
45191       default : break; // No points
45192       }
45193       return *this;
45194     }
45195 
45196     bool _draw_fill(const int x, const int y, const int z,
45197                     const CImg<T>& ref, const float tolerance2) const {
45198       const T *ptr1 = data(x,y,z), *ptr2 = ref._data;
45199       const unsigned long off = _width*_height*_depth;
45200       float diff = 0;
45201       cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; }
45202       return diff<=tolerance2;
45203     }
45204 
45205     //! Draw filled 3d region with the flood fill algorithm.
45206     /**
45207        \param x0 X-coordinate of the starting point of the region to fill.
45208        \param y0 Y-coordinate of the starting point of the region to fill.
45209        \param z0 Z-coordinate of the starting point of the region to fill.
45210        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
45211        \param[out] region Image that will contain the mask of the filled region mask, as an output.
45212        \param tolerance Tolerance concerning neighborhood values.
45213        \param opacity Opacity of the drawing.
45214        \param is_high_connectivity Tells if 8-connexity must be used.
45215        \return \c region is initialized with the binary mask of the filled region.
45216     **/
45217     template<typename tc, typename t>
45218     CImg<T>& draw_fill(const int x0, const int y0, const int z0,
45219                         const tc *const color, const float opacity,
45220                         CImg<t> &region,
45221                         const float tolerance = 0,
45222                         const bool is_high_connectivity = false) {
45223 #define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \
45224                                stack[N] = x; stack(N,1) = y; stack(N++,2) = z
45225 #define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2)
45226 #define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2)
45227 
45228       if (!containsXYZC(x0,y0,z0,0)) return *this;
45229       const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.0f);
45230       const float tolerance2 = cimg::sqr(tolerance);
45231       const CImg<T> ref = get_vector_at(x0,y0,z0);
45232       CImg<uintT> stack(256,1,1,3);
45233       CImg<ucharT> _region(_width,_height,_depth,1,0);
45234       unsigned int N = 0;
45235       int x, y, z;
45236 
45237       _draw_fill_push(x0,y0,z0);
45238       while (N>0) {
45239         _draw_fill_pop(x,y,z);
45240         if (!_region(x,y,z)) {
45241           const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1;
45242           int xl = x, xr = x;
45243 
45244           // Using these booleans reduces the number of pushes drastically.
45245           bool is_yp = false, is_yn = false, is_zp = false, is_zn = false;
45246           for (int step = -1; step<2; step+=2) {
45247             while (x>=0 && x<width() && _draw_fill_is_inside(x,y,z)) {
45248               if (yp>=0 && _draw_fill_is_inside(x,yp,z)) {
45249                 if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; }
45250               } else is_yp = false;
45251               if (yn<height() && _draw_fill_is_inside(x,yn,z)) {
45252                 if (!is_yn) { _draw_fill_push(x,yn,z); is_yn = true; }
45253               } else is_yn = false;
45254               if (depth()>1) {
45255                 if (zp>=0 && _draw_fill_is_inside(x,y,zp)) {
45256                   if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; }
45257                 } else is_zp = false;
45258                 if (zn<depth() && _draw_fill_is_inside(x,y,zn)) {
45259                   if (!is_zn) { _draw_fill_push(x,y,zn); is_zn = true; }
45260                 } else is_zn = false;
45261               }
45262               if (is_high_connectivity) {
45263                 const int xp = x - 1, xn = x + 1;
45264                 if (yp>=0 && !is_yp) {
45265                   if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) {
45266                     _draw_fill_push(xp,yp,z); if (step<0) is_yp = true;
45267                   }
45268                   if (xn<width() && _draw_fill_is_inside(xn,yp,z)) {
45269                     _draw_fill_push(xn,yp,z); if (step>0) is_yp = true;
45270                   }
45271                 }
45272                 if (yn<height() && !is_yn) {
45273                   if (xp>=0 && _draw_fill_is_inside(xp,yn,z)) {
45274                     _draw_fill_push(xp,yn,z); if (step<0) is_yn = true;
45275                   }
45276                   if (xn<width() && _draw_fill_is_inside(xn,yn,z)) {
45277                     _draw_fill_push(xn,yn,z); if (step>0) is_yn = true;
45278                   }
45279                 }
45280                 if (depth()>1) {
45281                   if (zp>=0 && !is_zp) {
45282                     if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) {
45283                       _draw_fill_push(xp,y,zp); if (step<0) is_zp = true;
45284                     }
45285                     if (xn<width() && _draw_fill_is_inside(xn,y,zp)) {
45286                       _draw_fill_push(xn,y,zp); if (step>0) is_zp = true;
45287                     }
45288 
45289                     if (yp>=0 && !is_yp) {
45290                       if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); }
45291                       if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); }
45292                       if (xn<width() && _draw_fill_is_inside(xn,yp,zp)) { _draw_fill_push(xn,yp,zp); }
45293                     }
45294                     if (yn<height() && !is_yn) {
45295                       if (_draw_fill_is_inside(x,yn,zp)) { _draw_fill_push(x,yn,zp); }
45296                       if (xp>=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); }
45297                       if (xn<width() && _draw_fill_is_inside(xn,yn,zp)) { _draw_fill_push(xn,yn,zp); }
45298                     }
45299                   }
45300 
45301                   if (zn<depth() && !is_zn) {
45302                     if (xp>=0 && _draw_fill_is_inside(xp,y,zn)) {
45303                       _draw_fill_push(xp,y,zn); if (step<0) is_zn = true;
45304                     }
45305                     if (xn<width() && _draw_fill_is_inside(xn,y,zn)) {
45306                       _draw_fill_push(xn,y,zn); if (step>0) is_zn = true;
45307                     }
45308 
45309                     if (yp>=0 && !is_yp) {
45310                       if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); }
45311                       if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); }
45312                       if (xn<width() && _draw_fill_is_inside(xn,yp,zn)) { _draw_fill_push(xn,yp,zn); }
45313                     }
45314                     if (yn<height() && !is_yn) {
45315                       if (_draw_fill_is_inside(x,yn,zn)) { _draw_fill_push(x,yn,zn); }
45316                       if (xp>=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); }
45317                       if (xn<width() && _draw_fill_is_inside(xn,yn,zn)) { _draw_fill_push(xn,yn,zn); }
45318                     }
45319                   }
45320                 }
45321               }
45322               x+=step;
45323             }
45324             if (step<0) { xl = ++x; x = xr + 1; is_yp = is_yn = is_zp = is_zn = false; }
45325             else xr = --x;
45326           }
45327           std::memset(_region.data(xl,y,z),1,xr - xl + 1);
45328           if (opacity==1) {
45329             if (sizeof(T)==1) {
45330               const int dx = xr - xl + 1;
45331               cimg_forC(*this,c) std::memset(data(xl,y,z,c),(int)color[c],dx);
45332             } else cimg_forC(*this,c) {
45333                 const T val = (T)color[c];
45334                 T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) *(ptri++) = val;
45335               }
45336           } else cimg_forC(*this,c) {
45337               const T val = (T)(color[c]*nopacity);
45338               T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) { *ptri = (T)(val + *ptri*copacity); ++ptri; }
45339             }
45340         }
45341       }
45342       _region.move_to(region);
45343       return *this;
45344     }
45345 
45346     //! Draw filled 3d region with the flood fill algorithm \simplification.
45347     template<typename tc>
45348     CImg<T>& draw_fill(const int x0, const int y0, const int z0,
45349                        const tc *const color, const float opacity=1,
45350                        const float tolerance=0, const bool is_high_connexity=false) {
45351       CImg<ucharT> tmp;
45352       return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity);
45353     }
45354 
45355     //! Draw filled 2d region with the flood fill algorithm \simplification.
45356     template<typename tc>
45357     CImg<T>& draw_fill(const int x0, const int y0,
45358                        const tc *const color, const float opacity=1,
45359                        const float tolerance=0, const bool is_high_connexity=false) {
45360       CImg<ucharT> tmp;
45361       return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity);
45362     }
45363 
45364     //! Draw a random plasma texture.
45365     /**
45366        \param alpha Alpha-parameter.
45367        \param beta Beta-parameter.
45368        \param scale Scale-parameter.
45369        \note Use the mid-point algorithm to render.
45370     **/
45371     CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
45372       if (is_empty()) return *this;
45373       const int w = width(), h = height();
45374       const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
45375       cimg_forZC(*this,z,c) {
45376         CImg<T> ref = get_shared_slice(z,c);
45377         for (int delta = 1<<std::min(scale,31U); delta>1; delta>>=1) {
45378           const int delta2 = delta>>1;
45379           const float r = alpha*delta + beta;
45380 
45381           // Square step.
45382           for (int y0 = 0; y0<h; y0+=delta)
45383             for (int x0 = 0; x0<w; x0+=delta) {
45384               const int x1 = (x0 + delta)%w, y1 = (y0 + delta)%h, xc = (x0 + delta2)%w, yc = (y0 + delta2)%h;
45385               const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) +
45386                                           r*cimg::rand(-1,1));
45387               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
45388             }
45389 
45390           // Diamond steps.
45391           for (int y = -delta2; y<h; y+=delta)
45392             for (int x0=0; x0<w; x0+=delta) {
45393               const int y0 = cimg::mod(y,h), x1 = (x0 + delta)%w, y1 = (y + delta)%h,
45394                 xc = (x0 + delta2)%w, yc = (y + delta2)%h;
45395               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
45396                                           r*cimg::rand(-1,1));
45397               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
45398             }
45399           for (int y0 = 0; y0<h; y0+=delta)
45400             for (int x = -delta2; x<w; x+=delta) {
45401               const int x0 = cimg::mod(x,w), x1 = (x + delta)%w, y1 = (y0 + delta)%h,
45402                 xc = (x + delta2)%w, yc = (y0 + delta2)%h;
45403               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
45404                                           r*cimg::rand(-1,1));
45405               ref(xc,yc) = (T)(val<m?m:val>M?M:val);
45406             }
45407           for (int y = -delta2; y<h; y+=delta)
45408             for (int x = -delta2; x<w; x+=delta) {
45409               const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + delta)%w, y1 = (y + delta)%h,
45410                 xc = (x + delta2)%w, yc = (y + delta2)%h;
45411               const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
45412                                           r*cimg::rand(-1,1));
45413                 ref(xc,yc) = (T)(val<m?m:val>M?M:val);
45414             }
45415         }
45416       }
45417       return *this;
45418     }
45419 
45420     //! Draw a quadratic Mandelbrot or Julia 2d fractal.
45421     /**
45422        \param x0 X-coordinate of the upper-left pixel.
45423        \param y0 Y-coordinate of the upper-left pixel.
45424        \param x1 X-coordinate of the lower-right pixel.
45425        \param y1 Y-coordinate of the lower-right pixel.
45426        \param colormap Colormap.
45427        \param opacity Drawing opacity.
45428        \param z0r Real part of the upper-left fractal vertex.
45429        \param z0i Imaginary part of the upper-left fractal vertex.
45430        \param z1r Real part of the lower-right fractal vertex.
45431        \param z1i Imaginary part of the lower-right fractal vertex.
45432        \param iteration_max Maximum number of iterations for each estimated point.
45433        \param is_normalized_iteration Tells if iterations are normalized.
45434        \param is_julia_set Tells if the Mandelbrot or Julia set is rendered.
45435        \param param_r Real part of the Julia set parameter.
45436        \param param_i Imaginary part of the Julia set parameter.
45437        \note Fractal rendering is done by the Escape Time Algorithm.
45438     **/
45439     template<typename tc>
45440     CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
45441                              const CImg<tc>& colormap, const float opacity=1,
45442                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
45443                              const unsigned int iteration_max=255,
45444                              const bool is_normalized_iteration=false,
45445                              const bool is_julia_set=false,
45446                              const double param_r=0, const double param_i=0) {
45447       if (is_empty()) return *this;
45448       CImg<tc> palette;
45449       if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
45450       if (palette && palette._spectrum!=_spectrum)
45451         throw CImgArgumentException(_cimg_instance
45452                                     "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have "
45453                                     "incompatible dimensions.",
45454                                     cimg_instance,
45455                                     colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
45456 
45457       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f), ln2 = (float)std::log(2.0);
45458       const int
45459         _x0 = cimg::cut(x0,0,width() - 1),
45460         _y0 = cimg::cut(y0,0,height() - 1),
45461         _x1 = cimg::cut(x1,0,width() - 1),
45462         _y1 = cimg::cut(y1,0,height() - 1);
45463 
45464       cimg_pragma_openmp(parallel for collapse(2) cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=2048))
45465       for (int q = _y0; q<=_y1; ++q)
45466         for (int p = _x0; p<=_x1; ++p) {
45467           unsigned int iteration = 0;
45468           const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
45469           double zr, zi, cr, ci;
45470           if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
45471           else { zr = param_r; zi = param_i; cr = x; ci = y; }
45472           for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
45473             const double temp = zr*zr - zi*zi + cr;
45474             zi = 2*zr*zi + ci;
45475             zr = temp;
45476           }
45477           if (iteration>iteration_max) {
45478             if (palette) {
45479               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
45480               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
45481             } else {
45482               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
45483               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
45484             }
45485           } else if (is_normalized_iteration) {
45486             const float
45487               normz = (float)cimg::abs(zr*zr + zi*zi),
45488               niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
45489             if (palette) {
45490               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
45491               else cimg_forC(*this,c)
45492                      (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
45493             } else {
45494               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
45495               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
45496             }
45497           } else {
45498             if (palette) {
45499               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
45500               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
45501             } else {
45502               if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
45503               else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
45504             }
45505           }
45506         }
45507       return *this;
45508     }
45509 
45510     //! Draw a quadratic Mandelbrot or Julia 2d fractal \overloading.
45511     template<typename tc>
45512     CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
45513                              const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
45514                              const unsigned int iteration_max=255,
45515                              const bool is_normalized_iteration=false,
45516                              const bool is_julia_set=false,
45517                              const double param_r=0, const double param_i=0) {
45518       return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity,
45519                              z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
45520     }
45521 
45522     //! Draw a 1d gaussian function.
45523     /**
45524        \param xc X-coordinate of the gaussian center.
45525        \param sigma Standard variation of the gaussian distribution.
45526        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
45527        \param opacity Drawing opacity.
45528     **/
45529     template<typename tc>
45530     CImg<T>& draw_gaussian(const float xc, const float sigma,
45531                            const tc *const color, const float opacity=1) {
45532       if (is_empty()) return *this;
45533       if (!color)
45534         throw CImgArgumentException(_cimg_instance
45535                                     "draw_gaussian(): Specified color is (null).",
45536                                     cimg_instance);
45537       const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
45538       const ulongT whd = (ulongT)_width*_height*_depth;
45539       const tc *col = color;
45540       cimg_forX(*this,x) {
45541         const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
45542         T *ptrd = data(x,0,0,0);
45543         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
45544         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
45545         col-=_spectrum;
45546       }
45547       return *this;
45548     }
45549 
45550     //! Draw a 2d gaussian function.
45551     /**
45552        \param xc X-coordinate of the gaussian center.
45553        \param yc Y-coordinate of the gaussian center.
45554        \param tensor Covariance matrix (must be 2x2).
45555        \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
45556        \param opacity Drawing opacity.
45557     **/
45558     template<typename t, typename tc>
45559     CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
45560                            const tc *const color, const float opacity=1) {
45561       if (is_empty()) return *this;
45562       if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
45563         throw CImgArgumentException(_cimg_instance
45564                                     "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
45565                                     cimg_instance,
45566                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
45567       if (!color)
45568         throw CImgArgumentException(_cimg_instance
45569                                     "draw_gaussian(): Specified color is (null).",
45570                                     cimg_instance);
45571       typedef typename CImg<t>::Tfloat tfloat;
45572       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
45573       const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
45574       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
45575       const ulongT whd = (ulongT)_width*_height*_depth;
45576       const tc *col = color;
45577       float dy = -yc;
45578       cimg_forY(*this,y) {
45579         float dx = -xc;
45580         cimg_forX(*this,x) {
45581           const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
45582           T *ptrd = data(x,y,0,0);
45583           if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
45584           else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
45585           col-=_spectrum;
45586           ++dx;
45587         }
45588         ++dy;
45589       }
45590       return *this;
45591     }
45592 
45593     //! Draw a 2d gaussian function \overloading.
45594     template<typename tc>
45595     CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
45596                            const tc *const color, const float opacity=1) {
45597       const double
45598         a = r1*ru*ru + r2*rv*rv,
45599         b = (r1-r2)*ru*rv,
45600         c = r1*rv*rv + r2*ru*ru;
45601       const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
45602       return draw_gaussian(xc,yc,tensor,color,opacity);
45603     }
45604 
45605     //! Draw a 2d gaussian function \overloading.
45606     template<typename tc>
45607     CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
45608                            const tc *const color, const float opacity=1) {
45609       return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
45610     }
45611 
45612     //! Draw a 3d gaussian function \overloading.
45613     template<typename t, typename tc>
45614     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
45615                            const tc *const color, const float opacity=1) {
45616       if (is_empty()) return *this;
45617       typedef typename CImg<t>::Tfloat tfloat;
45618       if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
45619         throw CImgArgumentException(_cimg_instance
45620                                     "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
45621                                     cimg_instance,
45622                                     tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
45623 
45624       const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
45625       const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = 2*invT2(2,0), d = invT2(1,1), e = 2*invT2(2,1), f = invT2(2,2);
45626       const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.0f);
45627       const ulongT whd = (ulongT)_width*_height*_depth;
45628       const tc *col = color;
45629       cimg_forXYZ(*this,x,y,z) {
45630         const float
45631           dx = (x - xc), dy = (y - yc), dz = (z - zc),
45632           val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
45633         T *ptrd = data(x,y,z,0);
45634         if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
45635         else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
45636         col-=_spectrum;
45637       }
45638       return *this;
45639     }
45640 
45641     //! Draw a 3d gaussian function \overloading.
45642     template<typename tc>
45643     CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
45644                            const tc *const color, const float opacity=1) {
45645       return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
45646     }
45647 
45648     //! Draw a 3d object.
45649     /**
45650        \param x0 X-coordinate of the 3d object position
45651        \param y0 Y-coordinate of the 3d object position
45652        \param z0 Z-coordinate of the 3d object position
45653        \param vertices Image Nx3 describing 3d point coordinates
45654        \param primitives List of P primitives
45655        \param colors List of P color (or textures)
45656        \param opacities Image or list of P opacities
45657        \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
45658        \param is_double_sided Tells if object faces have two sides or are oriented.
45659        \param focale length of the focale (0 for parallel projection)
45660        \param lightx X-coordinate of the light
45661        \param lighty Y-coordinate of the light
45662        \param lightz Z-coordinate of the light
45663        \param specular_lightness Amount of specular light.
45664        \param specular_shininess Shininess of the object
45665     **/
45666     template<typename tp, typename tf, typename tc, typename to>
45667     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
45668                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45669                            const CImgList<tc>& colors, const CImg<to>& opacities,
45670                            const unsigned int render_type=4,
45671                            const bool is_double_sided=false, const float focale=700,
45672                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
45673                            const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
45674       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
45675                            is_double_sided,focale,lightx,lighty,lightz,
45676                            specular_lightness,specular_shininess,CImg<floatT>::empty());
45677     }
45678 
45679     //! Draw a 3d object \simplification.
45680     template<typename tp, typename tf, typename tc, typename to, typename tz>
45681     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
45682                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45683                            const CImgList<tc>& colors, const CImg<to>& opacities,
45684                            const unsigned int render_type,
45685                            const bool is_double_sided, const float focale,
45686                            const float lightx, const float lighty, const float lightz,
45687                            const float specular_lightness, const float specular_shininess,
45688                            CImg<tz>& zbuffer) {
45689       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
45690                             render_type,is_double_sided,focale,lightx,lighty,lightz,
45691                             specular_lightness,specular_shininess,1);
45692     }
45693 
45694 #ifdef cimg_use_board
45695     template<typename tp, typename tf, typename tc, typename to>
45696     CImg<T>& draw_object3d(LibBoard::Board& board,
45697                            const float x0, const float y0, const float z0,
45698                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45699                            const CImgList<tc>& colors, const CImg<to>& opacities,
45700                            const unsigned int render_type=4,
45701                            const bool is_double_sided=false, const float focale=700,
45702                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
45703                            const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
45704       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
45705                            is_double_sided,focale,lightx,lighty,lightz,
45706                            specular_lightness,specular_shininess,CImg<floatT>::empty());
45707     }
45708 
45709     template<typename tp, typename tf, typename tc, typename to, typename tz>
45710     CImg<T>& draw_object3d(LibBoard::Board& board,
45711                            const float x0, const float y0, const float z0,
45712                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45713                            const CImgList<tc>& colors, const CImg<to>& opacities,
45714                            const unsigned int render_type,
45715                            const bool is_double_sided, const float focale,
45716                            const float lightx, const float lighty, const float lightz,
45717                            const float specular_lightness, const float specular_shininess,
45718                            CImg<tz>& zbuffer) {
45719       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
45720                             render_type,is_double_sided,focale,lightx,lighty,lightz,
45721                             specular_lightness,specular_shininess,1);
45722     }
45723 #endif
45724 
45725     //! Draw a 3d object \simplification.
45726     template<typename tp, typename tf, typename tc, typename to>
45727     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
45728                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45729                            const CImgList<tc>& colors, const CImgList<to>& opacities,
45730                            const unsigned int render_type=4,
45731                            const bool is_double_sided=false, const float focale=700,
45732                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
45733                            const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
45734       return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
45735                            is_double_sided,focale,lightx,lighty,lightz,
45736                            specular_lightness,specular_shininess,CImg<floatT>::empty());
45737     }
45738 
45739     //! Draw a 3d object \simplification.
45740     template<typename tp, typename tf, typename tc, typename to, typename tz>
45741     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
45742                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45743                            const CImgList<tc>& colors, const CImgList<to>& opacities,
45744                            const unsigned int render_type,
45745                            const bool is_double_sided, const float focale,
45746                            const float lightx, const float lighty, const float lightz,
45747                            const float specular_lightness, const float specular_shininess,
45748                            CImg<tz>& zbuffer) {
45749       return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
45750                             render_type,is_double_sided,focale,lightx,lighty,lightz,
45751                             specular_lightness,specular_shininess,1);
45752     }
45753 
45754 #ifdef cimg_use_board
45755     template<typename tp, typename tf, typename tc, typename to>
45756     CImg<T>& draw_object3d(LibBoard::Board& board,
45757                            const float x0, const float y0, const float z0,
45758                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45759                            const CImgList<tc>& colors, const CImgList<to>& opacities,
45760                            const unsigned int render_type=4,
45761                            const bool is_double_sided=false, const float focale=700,
45762                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
45763                            const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
45764       return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
45765                            is_double_sided,focale,lightx,lighty,lightz,
45766                            specular_lightness,specular_shininess,CImg<floatT>::empty());
45767     }
45768 
45769     template<typename tp, typename tf, typename tc, typename to, typename tz>
45770     CImg<T>& draw_object3d(LibBoard::Board& board,
45771                            const float x0, const float y0, const float z0,
45772                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45773                            const CImgList<tc>& colors, const CImgList<to>& opacities,
45774                            const unsigned int render_type,
45775                            const bool is_double_sided, const float focale,
45776                            const float lightx, const float lighty, const float lightz,
45777                            const float specular_lightness, const float specular_shininess,
45778                            CImg<tz>& zbuffer) {
45779       return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
45780                             render_type,is_double_sided,focale,lightx,lighty,lightz,
45781                             specular_lightness,specular_shininess,1);
45782     }
45783 #endif
45784 
45785     //! Draw a 3d object \simplification.
45786     template<typename tp, typename tf, typename tc>
45787     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
45788                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45789                            const CImgList<tc>& colors,
45790                            const unsigned int render_type=4,
45791                            const bool is_double_sided=false, const float focale=700,
45792                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
45793                            const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
45794       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
45795                            render_type,is_double_sided,focale,lightx,lighty,lightz,
45796                            specular_lightness,specular_shininess,CImg<floatT>::empty());
45797     }
45798 
45799     //! Draw a 3d object \simplification.
45800     template<typename tp, typename tf, typename tc, typename tz>
45801     CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
45802                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45803                            const CImgList<tc>& colors,
45804                            const unsigned int render_type,
45805                            const bool is_double_sided, const float focale,
45806                            const float lightx, const float lighty, const float lightz,
45807                            const float specular_lightness, const float specular_shininess,
45808                            CImg<tz>& zbuffer) {
45809       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
45810                            render_type,is_double_sided,focale,lightx,lighty,lightz,
45811                            specular_lightness,specular_shininess,zbuffer);
45812     }
45813 
45814 #ifdef cimg_use_board
45815     template<typename tp, typename tf, typename tc, typename to>
45816     CImg<T>& draw_object3d(LibBoard::Board& board,
45817                            const float x0, const float y0, const float z0,
45818                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45819                            const CImgList<tc>& colors,
45820                            const unsigned int render_type=4,
45821                            const bool is_double_sided=false, const float focale=700,
45822                            const float lightx=0, const float lighty=0, const float lightz=-5e8,
45823                            const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
45824       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
45825                            render_type,is_double_sided,focale,lightx,lighty,lightz,
45826                            specular_lightness,specular_shininess,CImg<floatT>::empty());
45827     }
45828 
45829     template<typename tp, typename tf, typename tc, typename to, typename tz>
45830     CImg<T>& draw_object3d(LibBoard::Board& board,
45831                            const float x0, const float y0, const float z0,
45832                            const CImg<tp>& vertices, const CImgList<tf>& primitives,
45833                            const CImgList<tc>& colors,
45834                            const unsigned int render_type,
45835                            const bool is_double_sided, const float focale,
45836                            const float lightx, const float lighty, const float lightz,
45837                            const float specular_lightness, const float specular_shininess,
45838                            CImg<tz>& zbuffer) {
45839       return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
45840                            render_type,is_double_sided,focale,lightx,lighty,lightz,
45841                            specular_lightness,specular_shininess,zbuffer);
45842     }
45843 #endif
45844 
45845     template<typename t, typename to>
45846     static float __draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
45847       if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; }
45848       if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); }
45849       opacity.assign(opacities[n_primitive],true);
45850       return 1.0f;
45851     }
45852 
45853     template<typename t, typename to>
45854     static float __draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
45855       opacity.assign();
45856       return n_primitive>=opacities._width?1.0f:(float)opacities[n_primitive];
45857     }
45858 
45859     template<typename t>
45860     static float ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) {
45861       return n_primitive<opacities._width && opacities[n_primitive].size()==1?(float)opacities(n_primitive,0):1.0f;
45862     }
45863 
45864     template<typename t>
45865     static float ___draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive) {
45866       return n_primitive<opacities._width?(float)opacities[n_primitive]:1.0f;
45867     }
45868 
45869     template<typename tz, typename tp, typename tf, typename tc, typename to>
45870     CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
45871                             const float X, const float Y, const float Z,
45872                             const CImg<tp>& vertices,
45873                             const CImgList<tf>& primitives,
45874                             const CImgList<tc>& colors,
45875                             const to& opacities,
45876                             const unsigned int render_type,
45877                             const bool is_double_sided, const float focale,
45878                             const float lightx, const float lighty, const float lightz,
45879                             const float specular_lightness, const float specular_shininess,
45880                             const float sprite_scale) {
45881       typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
45882       typedef typename to::value_type _to;
45883       if (is_empty() || !vertices || !primitives) return *this;
45884       CImg<char> error_message(1024);
45885       if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
45886         throw CImgArgumentException(_cimg_instance
45887                                     "draw_object3d(): Invalid specified 3d object (%u,%u) (%s).",
45888                                     cimg_instance,vertices._width,primitives._width,error_message.data());
45889 #ifndef cimg_use_board
45890       if (pboard) return *this;
45891 #endif
45892       if (render_type==5) cimg::mutex(10);  // Static variable used in this case, breaks thread-safety.
45893 
45894       const float
45895         nspec = 1 - (specular_lightness<0.0f?0.0f:(specular_lightness>1.0f?1.0f:specular_lightness)),
45896         nspec2 = 1 + (specular_shininess<0.0f?0.0f:specular_shininess),
45897         nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
45898         nsl2 = 1 - 2*nsl1*nspec,
45899         nsl3 = nspec2 - nsl1 - nsl2;
45900 
45901       // Create light texture for phong-like rendering.
45902       CImg<floatT> light_texture;
45903       if (render_type==5) {
45904         if (colors._width>primitives._width) {
45905           static CImg<floatT> default_light_texture;
45906           static const tc *lptr = 0;
45907           static tc ref_values[64] = { 0 };
45908           const CImg<tc>& img = colors.back();
45909           bool is_same_texture = (lptr==img._data);
45910           if (is_same_texture)
45911             for (unsigned int r = 0, j = 0; j<8; ++j)
45912               for (unsigned int i = 0; i<8; ++i)
45913                 if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) {
45914                   is_same_texture = false; break;
45915                 }
45916           if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
45917             (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
45918             lptr = colors.back().data();
45919             for (unsigned int r = 0, j = 0; j<8; ++j)
45920               for (unsigned int i = 0; i<8; ++i)
45921                 ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum);
45922           }
45923           light_texture.assign(default_light_texture,true);
45924         } else {
45925           static CImg<floatT> default_light_texture;
45926           static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0;
45927           if (!default_light_texture ||
45928               lightx!=olightx || lighty!=olighty || lightz!=olightz ||
45929               specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) {
45930             default_light_texture.assign(512,512);
45931             const float
45932               dlx = lightx - X,
45933               dly = lighty - Y,
45934               dlz = lightz - Z,
45935               nl = cimg::hypot(dlx,dly,dlz),
45936               nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl),
45937               nly = (default_light_texture._height - 1)/2*(1 + dly/nl),
45938               white[] = { 1 };
45939             default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.0f,white);
45940             cimg_forXY(default_light_texture,x,y) {
45941               const float factor = default_light_texture(x,y);
45942               if (factor>nspec) default_light_texture(x,y) = std::min(2.0f,nsl1*factor*factor + nsl2*factor + nsl3);
45943             }
45944             default_light_texture.resize(-100,-100,1,_spectrum);
45945             olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess;
45946           }
45947           light_texture.assign(default_light_texture,true);
45948         }
45949       }
45950 
45951       // Compute 3d to 2d projection.
45952       CImg<tpfloat> projections(vertices._width,2);
45953       tpfloat parallzmin = cimg::type<tpfloat>::max();
45954       const float absfocale = focale?cimg::abs(focale):0;
45955       if (absfocale) {
45956         cimg_pragma_openmp(parallel for cimg_openmp_if(projections.size()>4096))
45957         cimg_forX(projections,l) { // Perspective projection
45958           const tpfloat
45959             x = (tpfloat)vertices(l,0),
45960             y = (tpfloat)vertices(l,1),
45961             z = (tpfloat)vertices(l,2);
45962           const tpfloat projectedz = z + Z + absfocale;
45963           projections(l,1) = Y + absfocale*y/projectedz;
45964           projections(l,0) = X + absfocale*x/projectedz;
45965         }
45966       } else {
45967         cimg_pragma_openmp(parallel for cimg_openmp_if(projections.size()>4096))
45968         cimg_forX(projections,l) { // Parallel projection
45969           const tpfloat
45970             x = (tpfloat)vertices(l,0),
45971             y = (tpfloat)vertices(l,1),
45972             z = (tpfloat)vertices(l,2);
45973           if (z<parallzmin) parallzmin = z;
45974           projections(l,1) = Y + y;
45975           projections(l,0) = X + x;
45976         }
45977       }
45978 
45979       const float _focale = absfocale?absfocale:(1e5f-parallzmin);
45980       float zmax = 0;
45981       if (zbuffer) zmax = vertices.get_shared_row(2).max();
45982 
45983       // Compute visible primitives.
45984       CImg<uintT> visibles(primitives._width,1,1,1,~0U);
45985       CImg<tpfloat> zrange(primitives._width);
45986       const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
45987       bool is_forward = zbuffer?true:false;
45988 
45989       cimg_pragma_openmp(parallel for cimg_openmp_if(primitives.size()>4096))
45990       cimglist_for(primitives,l) {
45991         const CImg<tf>& primitive = primitives[l];
45992         switch (primitive.size()) {
45993         case 1 : { // Point
45994           CImg<_to> _opacity;
45995           __draw_object3d(opacities,l,_opacity);
45996           if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false;
45997           const unsigned int i0 = (unsigned int)primitive(0);
45998           const tpfloat z0 = Z + vertices(i0,2);
45999           if (z0>zmin) {
46000             visibles(l) = (unsigned int)l;
46001             zrange(l) = z0;
46002           }
46003         } break;
46004         case 5 : { // Sphere
46005           const unsigned int
46006             i0 = (unsigned int)primitive(0),
46007             i1 = (unsigned int)primitive(1);
46008           const tpfloat
46009             Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
46010             Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
46011             Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
46012             _zc = Z + Zc,
46013             zc = _zc + _focale,
46014             xc = X + Xc*(absfocale?absfocale/zc:1),
46015             yc = Y + Yc*(absfocale?absfocale/zc:1),
46016             radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0),
46017                                       vertices(i1,1) - vertices(i0,1),
46018                                       vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1),
46019             xm = xc - radius,
46020             ym = yc - radius,
46021             xM = xc + radius,
46022             yM = yc + radius;
46023           if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
46024             visibles(l) = (unsigned int)l;
46025             zrange(l) = _zc;
46026           }
46027           is_forward = false;
46028         } break;
46029         case 2 : case 6 : { // Segment
46030           const unsigned int
46031             i0 = (unsigned int)primitive(0),
46032             i1 = (unsigned int)primitive(1);
46033           const tpfloat
46034             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
46035             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
46036           tpfloat xm, xM, ym, yM;
46037           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
46038           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
46039           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
46040             visibles(l) = (unsigned int)l;
46041             zrange(l) = (z0 + z1)/2;
46042           }
46043         } break;
46044         case 3 : case 9 : { // Triangle
46045           const unsigned int
46046             i0 = (unsigned int)primitive(0),
46047             i1 = (unsigned int)primitive(1),
46048             i2 = (unsigned int)primitive(2);
46049           const tpfloat
46050             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
46051             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
46052             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
46053           tpfloat xm, xM, ym, yM;
46054           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
46055           if (x2<xm) xm = x2;
46056           if (x2>xM) xM = x2;
46057           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
46058           if (y2<ym) ym = y2;
46059           if (y2>yM) yM = y2;
46060           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
46061             const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
46062             if (is_double_sided || d<0) {
46063               visibles(l) = (unsigned int)l;
46064               zrange(l) = (z0 + z1 + z2)/3;
46065             }
46066           }
46067         } break;
46068         case 4 : case 12 : { // Quadrangle
46069           const unsigned int
46070             i0 = (unsigned int)primitive(0),
46071             i1 = (unsigned int)primitive(1),
46072             i2 = (unsigned int)primitive(2),
46073             i3 = (unsigned int)primitive(3);
46074           const tpfloat
46075             x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
46076             x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
46077             x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
46078             x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
46079           tpfloat xm, xM, ym, yM;
46080           if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
46081           if (x2<xm) xm = x2;
46082           if (x2>xM) xM = x2;
46083           if (x3<xm) xm = x3;
46084           if (x3>xM) xM = x3;
46085           if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
46086           if (y2<ym) ym = y2;
46087           if (y2>yM) yM = y2;
46088           if (y3<ym) ym = y3;
46089           if (y3>yM) yM = y3;
46090           if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) {
46091             const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
46092             if (is_double_sided || d<0) {
46093               visibles(l) = (unsigned int)l;
46094               zrange(l) = (z0 + z1 + z2 + z3)/4;
46095             }
46096           }
46097         } break;
46098         default :
46099           if (render_type==5) cimg::mutex(10,0);
46100           throw CImgArgumentException(_cimg_instance
46101                                       "draw_object3d(): Invalid primitive[%u] with size %u "
46102                                       "(should have size 1,2,3,4,5,6,9 or 12).",
46103                                       cimg_instance,
46104                                       l,primitive.size());
46105         }
46106       }
46107 
46108       // Force transparent primitives to be drawn last when zbuffer is activated
46109       // (and if object contains no spheres or sprites).
46110       if (is_forward)
46111         cimglist_for(primitives,l)
46112           if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l);
46113 
46114       // Sort only visibles primitives.
46115       unsigned int *p_visibles = visibles._data;
46116       tpfloat *p_zrange = zrange._data;
46117       const tpfloat *ptrz = p_zrange;
46118       cimg_for(visibles,ptr,unsigned int) {
46119         if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; }
46120         ++ptrz;
46121       }
46122       const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data);
46123       if (!nb_visibles) {
46124         if (render_type==5) cimg::mutex(10,0);
46125         return *this;
46126       }
46127       CImg<uintT> permutations;
46128       CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward);
46129 
46130       // Compute light properties
46131       CImg<floatT> lightprops;
46132       switch (render_type) {
46133       case 3 : { // Flat Shading
46134         lightprops.assign(nb_visibles);
46135         cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
46136         cimg_forX(lightprops,l) {
46137           const CImg<tf>& primitive = primitives(visibles(permutations(l)));
46138           const unsigned int psize = (unsigned int)primitive.size();
46139           if (psize==3 || psize==4 || psize==9 || psize==12) {
46140             const unsigned int
46141               i0 = (unsigned int)primitive(0),
46142               i1 = (unsigned int)primitive(1),
46143               i2 = (unsigned int)primitive(2);
46144             const tpfloat
46145               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
46146               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
46147               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
46148               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
46149               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
46150               nx = dy1*dz2 - dz1*dy2,
46151               ny = dz1*dx2 - dx1*dz2,
46152               nz = dx1*dy2 - dy1*dx2,
46153               norm = 1e-5f + cimg::hypot(nx,ny,nz),
46154               lx = X + (x0 + x1 + x2)/3 - lightx,
46155               ly = Y + (y0 + y1 + y2)/3 - lighty,
46156               lz = Z + (z0 + z1 + z2)/3 - lightz,
46157               nl = 1e-5f + cimg::hypot(lx,ly,lz),
46158               factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
46159             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
46160           } else lightprops[l] = 1;
46161         }
46162       } break;
46163 
46164       case 4 : // Gouraud Shading
46165       case 5 : { // Phong-Shading
46166         CImg<tpfloat> vertices_normals(vertices._width,6,1,1,0);
46167         cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
46168         for (unsigned int l = 0; l<nb_visibles; ++l) {
46169           const CImg<tf>& primitive = primitives[visibles(l)];
46170           const unsigned int psize = (unsigned int)primitive.size();
46171           const bool
46172             triangle_flag = (psize==3) || (psize==9),
46173             quadrangle_flag = (psize==4) || (psize==12);
46174           if (triangle_flag || quadrangle_flag) {
46175             const unsigned int
46176               i0 = (unsigned int)primitive(0),
46177               i1 = (unsigned int)primitive(1),
46178               i2 = (unsigned int)primitive(2),
46179               i3 = quadrangle_flag?(unsigned int)primitive(3):0;
46180             const tpfloat
46181               x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
46182               x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
46183               x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
46184               dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
46185               dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
46186               nnx = dy1*dz2 - dz1*dy2,
46187               nny = dz1*dx2 - dx1*dz2,
46188               nnz = dx1*dy2 - dy1*dx2,
46189               norm = 1e-5f + cimg::hypot(nnx,nny,nnz),
46190               nx = nnx/norm,
46191               ny = nny/norm,
46192               nz = nnz/norm;
46193             unsigned int ix = 0, iy = 1, iz = 2;
46194             if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; }
46195             vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz;
46196             vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz;
46197             vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz;
46198             if (quadrangle_flag) {
46199               vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz;
46200             }
46201           }
46202         }
46203 
46204         if (is_double_sided) cimg_forX(vertices_normals,p) {
46205             const float
46206               nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2),
46207               nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5),
46208               n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1;
46209             if (n1>n0) {
46210               vertices_normals(p,0) = -nx1;
46211               vertices_normals(p,1) = -ny1;
46212               vertices_normals(p,2) = -nz1;
46213             }
46214           }
46215 
46216         if (render_type==4) {
46217           lightprops.assign(vertices._width);
46218           cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
46219           cimg_forX(lightprops,l) {
46220             const tpfloat
46221               nx = vertices_normals(l,0),
46222               ny = vertices_normals(l,1),
46223               nz = vertices_normals(l,2),
46224               norm = 1e-5f + cimg::hypot(nx,ny,nz),
46225               lx = X + vertices(l,0) - lightx,
46226               ly = Y + vertices(l,1) - lighty,
46227               lz = Z + vertices(l,2) - lightz,
46228               nl = 1e-5f + cimg::hypot(lx,ly,lz),
46229               factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
46230             lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
46231           }
46232         } else {
46233           const unsigned int
46234             lw2 = light_texture._width/2 - 1,
46235             lh2 = light_texture._height/2 - 1;
46236           lightprops.assign(vertices._width,2);
46237           cimg_pragma_openmp(parallel for cimg_openmp_if(nb_visibles>4096))
46238           cimg_forX(lightprops,l) {
46239             const tpfloat
46240               nx = vertices_normals(l,0),
46241               ny = vertices_normals(l,1),
46242               nz = vertices_normals(l,2),
46243               norm = 1e-5f + cimg::hypot(nx,ny,nz),
46244               nnx = nx/norm,
46245               nny = ny/norm;
46246             lightprops(l,0) = lw2*(1 + nnx);
46247             lightprops(l,1) = lh2*(1 + nny);
46248           }
46249         }
46250       } break;
46251       }
46252 
46253       // Draw visible primitives
46254       const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
46255       CImg<_to> _opacity;
46256 
46257       for (unsigned int l = 0; l<nb_visibles; ++l) {
46258         const unsigned int n_primitive = visibles(permutations(l));
46259         const CImg<tf>& primitive = primitives[n_primitive];
46260         const CImg<tc>
46261           &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
46262           _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?
46263             __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
46264           &color = _color?_color:(__color?__color:default_color);
46265         const tc *const pcolor = color._data;
46266         const float opacity = __draw_object3d(opacities,n_primitive,_opacity);
46267 
46268 #ifdef cimg_use_board
46269         LibBoard::Board &board = *(LibBoard::Board*)pboard;
46270 #endif
46271 
46272         switch (primitive.size()) {
46273         case 1 : { // Colored point or sprite
46274           const unsigned int n0 = (unsigned int)primitive[0];
46275           const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1);
46276 
46277           if (_opacity.is_empty()) { // Scalar opacity.
46278 
46279             if (color.size()==_spectrum) { // Colored point.
46280               draw_point(x0,y0,pcolor,opacity);
46281 #ifdef cimg_use_board
46282               if (pboard) {
46283                 board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46284                 board.drawDot((float)x0,height()-(float)y0);
46285               }
46286 #endif
46287             } else { // Sprite.
46288               const tpfloat z = Z + vertices(n0,2);
46289               const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
46290               const unsigned int
46291                 _sw = (unsigned int)(color._width*factor),
46292                 _sh = (unsigned int)(color._height*factor),
46293                 sw = _sw?_sw:1, sh = _sh?_sh:1;
46294               const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
46295               if (sw<=3*_width/2 && sh<=3*_height/2 &&
46296                   (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
46297                 const CImg<tc>
46298                   _sprite = (sw!=color._width || sh!=color._height)?
46299                     color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
46300                   &sprite = _sprite?_sprite:color;
46301                 draw_image(nx0,ny0,sprite,opacity);
46302 #ifdef cimg_use_board
46303                 if (pboard) {
46304                   board.setPenColorRGBi(128,128,128);
46305                   board.setFillColor(LibBoard::Color::Null);
46306                   board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
46307                 }
46308 #endif
46309               }
46310             }
46311           } else { // Opacity mask.
46312             const tpfloat z = Z + vertices(n0,2);
46313             const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
46314             const unsigned int
46315               _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor),
46316               _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor),
46317               sw = _sw?_sw:1, sh = _sh?_sh:1;
46318             const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
46319             if (sw<=3*_width/2 && sh<=3*_height/2 &&
46320                 (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
46321               const CImg<tc>
46322                 _sprite = (sw!=color._width || sh!=color._height)?
46323                   color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
46324                 &sprite = _sprite?_sprite:color;
46325               const CImg<_to>
46326                 _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?
46327                   _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(),
46328                 &nopacity = _nopacity?_nopacity:_opacity;
46329               draw_image(nx0,ny0,sprite,nopacity);
46330 #ifdef cimg_use_board
46331               if (pboard) {
46332                 board.setPenColorRGBi(128,128,128);
46333                 board.setFillColor(LibBoard::Color::Null);
46334                 board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
46335               }
46336 #endif
46337             }
46338           }
46339         } break;
46340         case 2 : { // Colored line
46341           const unsigned int
46342             n0 = (unsigned int)primitive[0],
46343             n1 = (unsigned int)primitive[1];
46344           const int
46345             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46346             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
46347           const float
46348             z0 = vertices(n0,2) + Z + _focale,
46349             z1 = vertices(n1,2) + Z + _focale;
46350           if (render_type) {
46351             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity);
46352             else draw_line(x0,y0,x1,y1,pcolor,opacity);
46353 #ifdef cimg_use_board
46354             if (pboard) {
46355               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46356               board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1);
46357             }
46358 #endif
46359           } else {
46360             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity);
46361 #ifdef cimg_use_board
46362             if (pboard) {
46363               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46364               board.drawDot((float)x0,height() - (float)y0);
46365               board.drawDot((float)x1,height() - (float)y1);
46366             }
46367 #endif
46368           }
46369         } break;
46370         case 5 : { // Colored sphere
46371           const unsigned int
46372             n0 = (unsigned int)primitive[0],
46373             n1 = (unsigned int)primitive[1],
46374             is_wireframe = (unsigned int)primitive[2];
46375           const float
46376             Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)),
46377             Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)),
46378             Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)),
46379             zc = Z + Zc + _focale,
46380             xc = X + Xc*(absfocale?absfocale/zc:1),
46381             yc = Y + Yc*(absfocale?absfocale/zc:1),
46382             radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0),
46383                                       vertices(n1,1) - vertices(n0,1),
46384                                       vertices(n1,2) - vertices(n0,2))*(absfocale?absfocale/zc:1);
46385           switch (render_type) {
46386           case 0 :
46387             draw_point((int)xc,(int)yc,pcolor,opacity);
46388 #ifdef cimg_use_board
46389             if (pboard) {
46390               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46391               board.drawDot(xc,height() - yc);
46392             }
46393 #endif
46394             break;
46395           case 1 :
46396             draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
46397 #ifdef cimg_use_board
46398             if (pboard) {
46399               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46400               board.setFillColor(LibBoard::Color::Null);
46401               board.drawCircle(xc,height() - yc,radius);
46402             }
46403 #endif
46404             break;
46405           default :
46406             if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
46407             else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity);
46408 #ifdef cimg_use_board
46409             if (pboard) {
46410               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46411               if (!is_wireframe) board.fillCircle(xc,height() - yc,radius);
46412               else {
46413                 board.setFillColor(LibBoard::Color::Null);
46414                 board.drawCircle(xc,height() - yc,radius);
46415               }
46416             }
46417 #endif
46418             break;
46419           }
46420         } break;
46421         case 6 : { // Textured line
46422           if (!__color) {
46423             if (render_type==5) cimg::mutex(10,0);
46424             throw CImgArgumentException(_cimg_instance
46425                                         "draw_object3d(): Undefined texture for line primitive [%u].",
46426                                         cimg_instance,n_primitive);
46427           }
46428           const unsigned int
46429             n0 = (unsigned int)primitive[0],
46430             n1 = (unsigned int)primitive[1];
46431           const int
46432             tx0 = (int)primitive[2], ty0 = (int)primitive[3],
46433             tx1 = (int)primitive[4], ty1 = (int)primitive[5],
46434             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46435             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
46436           const float
46437             z0 = vertices(n0,2) + Z + _focale,
46438             z1 = vertices(n1,2) + Z + _focale;
46439           if (render_type) {
46440             if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
46441             else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opacity);
46442 #ifdef cimg_use_board
46443             if (pboard) {
46444               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46445               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46446             }
46447 #endif
46448           } else {
46449             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
46450                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
46451               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
46452                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity);
46453 #ifdef cimg_use_board
46454             if (pboard) {
46455               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46456               board.drawDot((float)x0,height() - (float)y0);
46457               board.drawDot((float)x1,height() - (float)y1);
46458             }
46459 #endif
46460           }
46461         } break;
46462         case 3 : { // Colored triangle
46463           const unsigned int
46464             n0 = (unsigned int)primitive[0],
46465             n1 = (unsigned int)primitive[1],
46466             n2 = (unsigned int)primitive[2];
46467           const int
46468             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46469             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
46470             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
46471           const float
46472             z0 = vertices(n0,2) + Z + _focale,
46473             z1 = vertices(n1,2) + Z + _focale,
46474             z2 = vertices(n2,2) + Z + _focale;
46475           switch (render_type) {
46476           case 0 :
46477             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity);
46478 #ifdef cimg_use_board
46479             if (pboard) {
46480               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46481               board.drawDot((float)x0,height() - (float)y0);
46482               board.drawDot((float)x1,height() - (float)y1);
46483               board.drawDot((float)x2,height() - (float)y2);
46484             }
46485 #endif
46486             break;
46487           case 1 :
46488             if (zbuffer)
46489               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity).
46490                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity);
46491             else
46492               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity).
46493                 draw_line(x1,y1,x2,y2,pcolor,opacity);
46494 #ifdef cimg_use_board
46495             if (pboard) {
46496               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46497               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46498               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
46499               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
46500             }
46501 #endif
46502             break;
46503           case 2 :
46504             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity);
46505             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity);
46506 #ifdef cimg_use_board
46507             if (pboard) {
46508               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46509               board.fillTriangle((float)x0,height() - (float)y0,
46510                                  (float)x1,height() - (float)y1,
46511                                  (float)x2,height() - (float)y2);
46512             }
46513 #endif
46514             break;
46515           case 3 :
46516             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l));
46517             else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l));
46518 #ifdef cimg_use_board
46519             if (pboard) {
46520               const float lp = std::min(lightprops(l),1);
46521               board.setPenColorRGBi((unsigned char)(color[0]*lp),
46522                                      (unsigned char)(color[1]*lp),
46523                                      (unsigned char)(color[2]*lp),
46524                                      (unsigned char)(opacity*255));
46525               board.fillTriangle((float)x0,height() - (float)y0,
46526                                  (float)x1,height() - (float)y1,
46527                                  (float)x2,height() - (float)y2);
46528             }
46529 #endif
46530             break;
46531           case 4 :
46532             if (zbuffer)
46533               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,
46534                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
46535             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity);
46536 #ifdef cimg_use_board
46537             if (pboard) {
46538               board.setPenColorRGBi((unsigned char)(color[0]),
46539                                      (unsigned char)(color[1]),
46540                                      (unsigned char)(color[2]),
46541                                      (unsigned char)(opacity*255));
46542               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
46543                                          (float)x1,height() - (float)y1,lightprops(n1),
46544                                          (float)x2,height() - (float)y2,lightprops(n2));
46545             }
46546 #endif
46547             break;
46548           case 5 : {
46549             const unsigned int
46550               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
46551               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
46552               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1);
46553             if (zbuffer)
46554               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46555             else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
46556 #ifdef cimg_use_board
46557             if (pboard) {
46558               const float
46559                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
46560                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
46561                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
46562                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
46563                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
46564                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
46565               board.setPenColorRGBi((unsigned char)(color[0]),
46566                                      (unsigned char)(color[1]),
46567                                      (unsigned char)(color[2]),
46568                                      (unsigned char)(opacity*255));
46569               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
46570                                          (float)x1,height() - (float)y1,l1,
46571                                          (float)x2,height() - (float)y2,l2);
46572             }
46573 #endif
46574           } break;
46575           }
46576         } break;
46577         case 4 : { // Colored quadrangle
46578           const unsigned int
46579             n0 = (unsigned int)primitive[0],
46580             n1 = (unsigned int)primitive[1],
46581             n2 = (unsigned int)primitive[2],
46582             n3 = (unsigned int)primitive[3];
46583           const int
46584             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46585             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
46586             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
46587             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1),
46588             xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4;
46589           const float
46590             z0 = vertices(n0,2) + Z + _focale,
46591             z1 = vertices(n1,2) + Z + _focale,
46592             z2 = vertices(n2,2) + Z + _focale,
46593             z3 = vertices(n3,2) + Z + _focale,
46594             zc = (z0 + z1 + z2 + z3)/4;
46595 
46596           switch (render_type) {
46597           case 0 :
46598             draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).
46599               draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity);
46600 #ifdef cimg_use_board
46601             if (pboard) {
46602               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46603               board.drawDot((float)x0,height() - (float)y0);
46604               board.drawDot((float)x1,height() - (float)y1);
46605               board.drawDot((float)x2,height() - (float)y2);
46606               board.drawDot((float)x3,height() - (float)y3);
46607             }
46608 #endif
46609             break;
46610           case 1 :
46611             if (zbuffer)
46612               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity).
46613                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity);
46614             else
46615               draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity).
46616                 draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity);
46617 #ifdef cimg_use_board
46618             if (pboard) {
46619               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46620               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46621               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
46622               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
46623               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
46624             }
46625 #endif
46626             break;
46627           case 2 :
46628             if (zbuffer)
46629               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).
46630                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity);
46631             else
46632               draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity);
46633 #ifdef cimg_use_board
46634             if (pboard) {
46635               board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
46636               board.fillTriangle((float)x0,height() - (float)y0,
46637                                  (float)x1,height() - (float)y1,
46638                                  (float)x2,height() - (float)y2);
46639               board.fillTriangle((float)x0,height() - (float)y0,
46640                                  (float)x2,height() - (float)y2,
46641                                  (float)x3,height() - (float)y3);
46642             }
46643 #endif
46644             break;
46645           case 3 :
46646             if (zbuffer)
46647               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)).
46648                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l));
46649             else
46650               _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)).
46651                 _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l));
46652 #ifdef cimg_use_board
46653             if (pboard) {
46654               const float lp = std::min(lightprops(l),1);
46655               board.setPenColorRGBi((unsigned char)(color[0]*lp),
46656                                      (unsigned char)(color[1]*lp),
46657                                      (unsigned char)(color[2]*lp),(unsigned char)(opacity*255));
46658               board.fillTriangle((float)x0,height() - (float)y0,
46659                                  (float)x1,height() - (float)y1,
46660                                  (float)x2,height() - (float)y2);
46661               board.fillTriangle((float)x0,height() - (float)y0,
46662                                  (float)x2,height() - (float)y2,
46663                                  (float)x3,height() - (float)y3);
46664             }
46665 #endif
46666             break;
46667           case 4 : {
46668             const float
46669               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
46670               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3),
46671               lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4;
46672             if (zbuffer)
46673               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
46674               draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
46675               draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
46676                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
46677             else
46678               draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
46679               draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
46680               draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
46681                 draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
46682 
46683 #ifdef cimg_use_board
46684             if (pboard) {
46685               board.setPenColorRGBi((unsigned char)(color[0]),
46686                                      (unsigned char)(color[1]),
46687                                      (unsigned char)(color[2]),
46688                                      (unsigned char)(opacity*255));
46689               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
46690                                          (float)x1,height() - (float)y1,lightprop1,
46691                                          (float)x2,height() - (float)y2,lightprop2);
46692               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
46693                                          (float)x2,height() - (float)y2,lightprop2,
46694                                          (float)x3,height() - (float)y3,lightprop3);
46695             }
46696 #endif
46697           } break;
46698           case 5 : {
46699             const unsigned int
46700               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
46701               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
46702               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
46703               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1),
46704               lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4;
46705             if (zbuffer)
46706               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
46707               draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
46708               draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
46709                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
46710             else
46711               draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
46712               draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
46713               draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
46714                 draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
46715 
46716 #ifdef cimg_use_board
46717             if (pboard) {
46718               const float
46719                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
46720                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
46721                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
46722                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
46723               board.setPenColorRGBi((unsigned char)(color[0]),
46724                                      (unsigned char)(color[1]),
46725                                      (unsigned char)(color[2]),
46726                                      (unsigned char)(opacity*255));
46727               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
46728                                          (float)x1,height() - (float)y1,l1,
46729                                          (float)x2,height() - (float)y2,l2);
46730               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
46731                                          (float)x2,height() - (float)y2,l2,
46732                                          (float)x3,height() - (float)y3,l3);
46733             }
46734 #endif
46735           } break;
46736           }
46737         } break;
46738         case 9 : { // Textured triangle
46739           if (!__color) {
46740             if (render_type==5) cimg::mutex(10,0);
46741             throw CImgArgumentException(_cimg_instance
46742                                         "draw_object3d(): Undefined texture for triangle primitive [%u].",
46743                                         cimg_instance,n_primitive);
46744           }
46745           const unsigned int
46746             n0 = (unsigned int)primitive[0],
46747             n1 = (unsigned int)primitive[1],
46748             n2 = (unsigned int)primitive[2];
46749           const int
46750             tx0 = (int)primitive[3], ty0 = (int)primitive[4],
46751             tx1 = (int)primitive[5], ty1 = (int)primitive[6],
46752             tx2 = (int)primitive[7], ty2 = (int)primitive[8],
46753             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46754             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
46755             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
46756           const float
46757             z0 = vertices(n0,2) + Z + _focale,
46758             z1 = vertices(n1,2) + Z + _focale,
46759             z2 = vertices(n2,2) + Z + _focale;
46760           switch (render_type) {
46761           case 0 :
46762             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
46763                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
46764               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
46765                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
46766               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
46767                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity);
46768 #ifdef cimg_use_board
46769             if (pboard) {
46770               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46771               board.drawDot((float)x0,height() - (float)y0);
46772               board.drawDot((float)x1,height() - (float)y1);
46773               board.drawDot((float)x2,height() - (float)y2);
46774             }
46775 #endif
46776             break;
46777           case 1 :
46778             if (zbuffer)
46779               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
46780                 draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
46781                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
46782             else
46783               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
46784                 draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
46785                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
46786 #ifdef cimg_use_board
46787             if (pboard) {
46788               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46789               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46790               board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
46791               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
46792             }
46793 #endif
46794             break;
46795           case 2 :
46796             if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
46797             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
46798 #ifdef cimg_use_board
46799             if (pboard) {
46800               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46801               board.fillTriangle((float)x0,height() - (float)y0,
46802                                  (float)x1,height() - (float)y1,
46803                                  (float)x2,height() - (float)y2);
46804             }
46805 #endif
46806             break;
46807           case 3 :
46808             if (zbuffer)
46809               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
46810             else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
46811 #ifdef cimg_use_board
46812             if (pboard) {
46813               const float lp = std::min(lightprops(l),1);
46814               board.setPenColorRGBi((unsigned char)(128*lp),
46815                                     (unsigned char)(128*lp),
46816                                     (unsigned char)(128*lp),
46817                                     (unsigned char)(opacity*255));
46818               board.fillTriangle((float)x0,height() - (float)y0,
46819                                  (float)x1,height() - (float)y1,
46820                                  (float)x2,height() - (float)y2);
46821             }
46822 #endif
46823             break;
46824           case 4 :
46825             if (zbuffer)
46826               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
46827                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
46828             else
46829               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
46830                             lightprops(n0),lightprops(n1),lightprops(n2),opacity);
46831 #ifdef cimg_use_board
46832             if (pboard) {
46833               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46834               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
46835                                         (float)x1,height() - (float)y1,lightprops(n1),
46836                                         (float)x2,height() - (float)y2,lightprops(n2));
46837             }
46838 #endif
46839             break;
46840           case 5 :
46841             if (zbuffer)
46842               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
46843                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
46844                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
46845                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
46846                             opacity);
46847             else
46848               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
46849                             (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
46850                             (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
46851                             (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
46852                             opacity);
46853 #ifdef cimg_use_board
46854             if (pboard) {
46855               const float
46856                 l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
46857                                    (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
46858                 l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
46859                                    (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
46860                 l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
46861                                    (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
46862               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46863               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
46864                                         (float)x1,height() - (float)y1,l1,
46865                                         (float)x2,height() - (float)y2,l2);
46866             }
46867 #endif
46868             break;
46869           }
46870         } break;
46871         case 12 : { // Textured quadrangle
46872           if (!__color) {
46873             if (render_type==5) cimg::mutex(10,0);
46874             throw CImgArgumentException(_cimg_instance
46875                                         "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
46876                                         cimg_instance,n_primitive);
46877           }
46878           const unsigned int
46879             n0 = (unsigned int)primitive[0],
46880             n1 = (unsigned int)primitive[1],
46881             n2 = (unsigned int)primitive[2],
46882             n3 = (unsigned int)primitive[3];
46883           const int
46884             tx0 = (int)primitive[4], ty0 = (int)primitive[5],
46885             tx1 = (int)primitive[6], ty1 = (int)primitive[7],
46886             tx2 = (int)primitive[8], ty2 = (int)primitive[9],
46887             tx3 = (int)primitive[10], ty3 = (int)primitive[11],
46888             txc = (tx0 + tx1 + tx2 + tx3)/4, tyc = (ty0 + ty1 + ty2 + ty3)/4,
46889             x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
46890             x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
46891             x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
46892             x3 = (int)projections(n3,0), y3 = (int)projections(n3,1),
46893             xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4;
46894           const float
46895             z0 = vertices(n0,2) + Z + _focale,
46896             z1 = vertices(n1,2) + Z + _focale,
46897             z2 = vertices(n2,2) + Z + _focale,
46898             z3 = vertices(n3,2) + Z + _focale,
46899             zc = (z0 + z1 + z2 + z3)/4;
46900 
46901           switch (render_type) {
46902           case 0 :
46903             draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
46904                                                  ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
46905               draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
46906                                                    ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
46907               draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
46908                                                    ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity).
46909               draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3,
46910                                                    ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity);
46911 #ifdef cimg_use_board
46912             if (pboard) {
46913               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46914               board.drawDot((float)x0,height() - (float)y0);
46915               board.drawDot((float)x1,height() - (float)y1);
46916               board.drawDot((float)x2,height() - (float)y2);
46917               board.drawDot((float)x3,height() - (float)y3);
46918             }
46919 #endif
46920             break;
46921           case 1 :
46922             if (zbuffer)
46923               draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
46924                 draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
46925                 draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
46926                 draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
46927             else
46928               draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
46929                 draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
46930                 draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
46931                 draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
46932 #ifdef cimg_use_board
46933             if (pboard) {
46934               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46935               board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
46936               board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
46937               board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
46938               board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
46939             }
46940 #endif
46941             break;
46942           case 2 :
46943             if (zbuffer)
46944               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
46945                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
46946             else
46947               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
46948                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
46949 #ifdef cimg_use_board
46950             if (pboard) {
46951               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
46952               board.fillTriangle((float)x0,height() - (float)y0,
46953                                  (float)x1,height() - (float)y1,
46954                                  (float)x2,height() - (float)y2);
46955               board.fillTriangle((float)x0,height() - (float)y0,
46956                                  (float)x2,height() - (float)y2,
46957                                  (float)x3,height() - (float)y3);
46958             }
46959 #endif
46960             break;
46961           case 3 :
46962             if (zbuffer)
46963               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
46964                 draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
46965             else
46966               draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
46967                 draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
46968 #ifdef cimg_use_board
46969             if (pboard) {
46970               const float lp = std::min(lightprops(l),1);
46971               board.setPenColorRGBi((unsigned char)(128*lp),
46972                                      (unsigned char)(128*lp),
46973                                      (unsigned char)(128*lp),
46974                                      (unsigned char)(opacity*255));
46975               board.fillTriangle((float)x0,height() - (float)y0,
46976                                  (float)x1,height() - (float)y1,
46977                                  (float)x2,height() - (float)y2);
46978               board.fillTriangle((float)x0,height() - (float)y0,
46979                                  (float)x2,height() - (float)y2,
46980                                  (float)x3,height() - (float)y3);
46981             }
46982 #endif
46983             break;
46984           case 4 : {
46985             const float
46986               lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
46987               lightprop2 = lightprops(n2), lightprop3 = lightprops(n3),
46988               lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop3)/4;
46989             if (zbuffer)
46990               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,color,tx0,ty0,tx1,ty1,txc,tyc,
46991                             lightprop0,lightprop1,lightpropc,opacity).
46992                 draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,color,tx1,ty1,tx2,ty2,txc,tyc,
46993                               lightprop1,lightprop2,lightpropc,opacity).
46994                 draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,color,tx2,ty2,tx3,ty3,txc,tyc,
46995                               lightprop2,lightprop3,lightpropc,opacity).
46996                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,color,tx3,ty3,tx0,ty0,txc,tyc,
46997                               lightprop3,lightprop0,lightpropc,opacity);
46998             else
46999               draw_triangle(x0,y0,z0,x1,y1,z1,xc,yc,zc,color,tx0,ty0,tx1,ty1,txc,tyc,
47000                             lightprop0,lightprop1,lightpropc,opacity).
47001                 draw_triangle(x1,y1,z1,x2,y2,z2,xc,yc,zc,color,tx1,ty1,tx2,ty2,txc,tyc,
47002                               lightprop1,lightprop2,lightpropc,opacity).
47003                 draw_triangle(x2,y2,z2,x3,y3,z3,xc,yc,zc,color,tx2,ty2,tx3,ty3,txc,tyc,
47004                               lightprop2,lightprop3,lightpropc,opacity).
47005                 draw_triangle(x3,y3,z3,x0,y0,z0,xc,yc,zc,color,tx3,ty3,tx0,ty0,txc,tyc,
47006                               lightprop3,lightprop0,lightpropc,opacity);
47007 #ifdef cimg_use_board
47008             if (pboard) {
47009               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
47010               board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
47011                                          (float)x1,height() - (float)y1,lightprop1,
47012                                          (float)x2,height() - (float)y2,lightprop2);
47013               board.fillGouraudTriangle((float)x0,height()  -(float)y0,lightprop0,
47014                                          (float)x2,height() - (float)y2,lightprop2,
47015                                          (float)x3,height() - (float)y3,lightprop3);
47016             }
47017 #endif
47018           } break;
47019           case 5 : {
47020             const unsigned int
47021               lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
47022               lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
47023               lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
47024               lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1),
47025               lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4;
47026             if (zbuffer)
47027               draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,color,tx0,ty0,tx1,ty1,txc,tyc,
47028                             light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
47029                 draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,color,tx1,ty1,tx2,ty2,txc,tyc,
47030                               light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
47031                 draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,color,tx2,ty2,tx3,ty3,txc,tyc,
47032                               light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
47033                 draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,color,tx3,ty3,tx0,ty0,txc,tyc,
47034                               light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
47035             else
47036               draw_triangle(x0,y0,z0,x1,y1,z1,xc,yc,zc,color,tx0,ty0,tx1,ty1,txc,tyc,
47037                             light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
47038                 draw_triangle(x1,y1,z1,x2,y2,z2,xc,yc,zc,color,tx1,ty1,tx2,ty2,txc,tyc,
47039                               light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
47040                 draw_triangle(x2,y2,z2,x3,y3,z3,xc,yc,zc,color,tx2,ty2,tx3,ty3,txc,tyc,
47041                               light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
47042                 draw_triangle(x3,y3,z3,x0,y0,z0,xc,yc,zc,color,tx3,ty3,tx0,ty0,txc,tyc,
47043                               light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
47044 #ifdef cimg_use_board
47045             if (pboard) {
47046               const float
47047                 l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
47048                 l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
47049                 l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
47050                 l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
47051               board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
47052               board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
47053                                          (float)x1,height() - (float)y1,l1,
47054                                          (float)x2,height() - (float)y2,l2);
47055               board.fillGouraudTriangle((float)x0,height()  -(float)y0,l0,
47056                                          (float)x2,height() - (float)y2,l2,
47057                                          (float)x3,height() - (float)y3,l3);
47058             }
47059 #endif
47060           } break;
47061           }
47062         } break;
47063         }
47064       }
47065       if (render_type==5) cimg::mutex(10,0);
47066       return *this;
47067     }
47068 
47069     //@}
47070     //---------------------------
47071     //
47072     //! \name Data Input
47073     //@{
47074     //---------------------------
47075 
47076     //! Launch simple interface to select a shape from an image.
47077     /**
47078        \param disp Display window to use.
47079        \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>.
47080        \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images.
47081        \param exit_on_anykey Exit function when any key is pressed.
47082     **/
47083     CImg<T>& select(CImgDisplay &disp,
47084 		    const unsigned int feature_type=2, unsigned int *const XYZ=0,
47085                     const bool exit_on_anykey=false,
47086                     const bool is_deep_selection_default=false) {
47087       return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
47088     }
47089 
47090     //! Simple interface to select a shape from an image \overloading.
47091     CImg<T>& select(const char *const title,
47092 		    const unsigned int feature_type=2, unsigned int *const XYZ=0,
47093                     const bool exit_on_anykey=false,
47094                     const bool is_deep_selection_default=false) {
47095       return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
47096     }
47097 
47098     //! Simple interface to select a shape from an image \newinstance.
47099     CImg<intT> get_select(CImgDisplay &disp,
47100 		          const unsigned int feature_type=2, unsigned int *const XYZ=0,
47101                           const bool exit_on_anykey=false,
47102                           const bool is_deep_selection_default=false) const {
47103       return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
47104     }
47105 
47106     //! Simple interface to select a shape from an image \newinstance.
47107     CImg<intT> get_select(const char *const title,
47108     			  const unsigned int feature_type=2, unsigned int *const XYZ=0,
47109                           const bool exit_on_anykey=false,
47110                           const bool is_deep_selection_default=false) const {
47111       CImgDisplay disp;
47112       return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
47113     }
47114 
47115     CImg<intT> _select(CImgDisplay &disp, const char *const title,
47116                        const unsigned int feature_type, unsigned int *const XYZ,
47117                        const int origX, const int origY, const int origZ,
47118                        const bool exit_on_anykey,
47119                        const bool reset_view3d,
47120                        const bool force_display_z_coord,
47121                        const bool is_deep_selection_default) const {
47122       if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
47123       if (!disp) {
47124         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
47125         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
47126       } else if (title) disp.set_title("%s",title);
47127 
47128       CImg<T> thumb;
47129       if (width()>disp.screen_width() || height()>disp.screen_height())
47130         get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb);
47131 
47132       const unsigned int old_normalization = disp.normalization();
47133       bool old_is_resized = disp.is_resized();
47134       disp._normalization = 0;
47135       disp.show().set_key(0).set_wheel().show_mouse();
47136 
47137       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
47138 
47139       int area = 0, area_started = 0, area_clicked = 0, phase = 0,
47140         X0 = (int)((XYZ?XYZ[0]:(_width - 1)/2)%_width),
47141         Y0 = (int)((XYZ?XYZ[1]:(_height - 1)/2)%_height),
47142         Z0 = (int)((XYZ?XYZ[2]:(_depth - 1)/2)%_depth),
47143         X1 =-1, Y1 = -1, Z1 = -1,
47144         X3d = -1, Y3d = -1,
47145         oX3d = X3d, oY3d = -1,
47146         omx = -1, omy = -1;
47147       float X = -1, Y = -1, Z = -1;
47148       unsigned int key = 0;
47149 
47150       bool is_deep_selection = is_deep_selection_default,
47151         shape_selected = false, text_down = false, visible_cursor = true;
47152       static CImg<floatT> pose3d;
47153       static bool is_view3d = false, is_axes = true;
47154       if (reset_view3d) { pose3d.assign(); is_view3d = false; }
47155       CImg<floatT> points3d, opacities3d, sel_opacities3d;
47156       CImgList<uintT> primitives3d, sel_primitives3d;
47157       CImgList<ucharT> colors3d, sel_colors3d;
47158       CImg<ucharT> visu, visu0, view3d;
47159       CImg<charT> text(1024); *text = 0;
47160 
47161       while (!key && !disp.is_closed() && !shape_selected) {
47162 
47163         // Handle mouse motion and selection
47164         int
47165           mx = disp.mouse_x(),
47166           my = disp.mouse_y();
47167 
47168         const float
47169           mX = mx<0?-1.0f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(),
47170           mY = my<0?-1.0f:(float)my*(height() + (depth()>1?depth():0))/disp.height();
47171 
47172         area = 0;
47173         if (mX>=0 && mY>=0 && mX<width() && mY<height())  { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); }
47174         if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); }
47175         if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); }
47176         if (mX>=width() && mY>=height()) area = 4;
47177         if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0;
47178 
47179         CImg<charT> filename(32);
47180 
47181         switch (key = disp.key()) {
47182 #if cimg_OS!=2
47183         case cimg::keyCTRLRIGHT :
47184 #endif
47185         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
47186         case cimg::keyPAGEUP :
47187           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
47188         case cimg::keyPAGEDOWN :
47189           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
47190         case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47191             is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign();
47192           } break;
47193         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47194             disp.set_fullscreen(false).
47195               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
47196                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
47197               _is_resized = true;
47198             disp.set_key(key,false); key = 0; visu0.assign();
47199           } break;
47200         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47201             disp.set_fullscreen(false).
47202               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
47203             disp.set_key(key,false); key = 0; visu0.assign();
47204           } break;
47205         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47206             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
47207             disp.set_key(key,false); key = 0; visu0.assign();
47208           } break;
47209         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47210             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
47211             disp.set_key(key,false); key = 0; visu0.assign();
47212           } break;
47213         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47214             is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign();
47215           } break;
47216         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47217             static unsigned int snap_number = 0;
47218             std::FILE *file;
47219             do {
47220               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
47221               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
47222             } while (file);
47223             if (visu0) {
47224               (+visu0).draw_text(0,0," Saving snapshot... ",foreground_color,background_color,0.7f,13).display(disp);
47225               visu0.save(filename);
47226               (+visu0).draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,0.7f,13,filename._data).
47227                 display(disp);
47228             }
47229             disp.set_key(key,false); key = 0;
47230           } break;
47231         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47232             static unsigned int snap_number = 0;
47233             std::FILE *file;
47234             do {
47235 #ifdef cimg_use_zlib
47236               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
47237 #else
47238               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
47239 #endif
47240               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
47241             } while (file);
47242             (+visu0).draw_text(0,0," Saving instance... ",foreground_color,background_color,0.7f,13).display(disp);
47243             save(filename);
47244             (+visu0).draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,0.7f,13,filename._data).
47245               display(disp);
47246             disp.set_key(key,false); key = 0;
47247           } break;
47248         }
47249 
47250         switch (area) {
47251 
47252         case 0 : // When mouse is out of image range.
47253           mx = my = -1; X = Y = Z = -1;
47254           break;
47255 
47256         case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections.
47257           const unsigned int but = disp.button();
47258           const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4);
47259 
47260           if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step).
47261             if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
47262             X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
47263           }
47264           if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes).
47265             switch (area_started) {
47266             case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break;
47267             case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break;
47268             case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break;
47269             }
47270           }
47271           if (b2 && area_clicked==area) { // When moving through the image/volume.
47272             if (phase) {
47273               if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
47274               X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
47275             } else {
47276               if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign();
47277               X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z;
47278             }
47279           }
47280           if (b3) { // Reset selection
47281             X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0;
47282             visu0.assign();
47283           }
47284           if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel).
47285             if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() &&
47286                 !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) {
47287               switch (area) {
47288               case 1 :
47289                 if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel());
47290                 visu0.assign(); break;
47291               case 2 :
47292                 if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel());
47293                 visu0.assign(); break;
47294               case 3 :
47295                 if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel());
47296                 visu0.assign(); break;
47297               }
47298               disp.set_wheel();
47299             } else key = ~0U;
47300           }
47301 
47302           if ((phase==0 && b1) ||
47303               (phase==1 && !b1) ||
47304               (phase==2 && b1)) switch (phase) { // Detect change of phase
47305             case 0 :
47306               if (area==area_clicked) {
47307                 X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase;
47308               } break;
47309             case 1 :
47310               if (area==area_started) {
47311                 X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase;
47312                 if (_depth>1) {
47313                   if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default;
47314                   if (is_deep_selection) ++phase;
47315                 }
47316               } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); }
47317               break;
47318             case 2 : ++phase; break;
47319             }
47320         } break;
47321 
47322         case 4 : // When mouse is over the 3d view.
47323           if (is_view3d && points3d) {
47324             X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0));
47325             Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0));
47326             if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
47327             // Left + right buttons: reset.
47328             if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; }
47329             else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate.
47330               const float
47331                 R = 0.45f*std::min(view3d._width,view3d._height),
47332                 R2 = R*R,
47333                 u0 = (float)(oX3d - view3d.width()/2),
47334                 v0 = (float)(oY3d - view3d.height()/2),
47335                 u1 = (float)(X3d - view3d.width()/2),
47336                 v1 = (float)(Y3d - view3d.height()/2),
47337                 n0 = cimg::hypot(u0,v0),
47338                 n1 = cimg::hypot(u1,v1),
47339                 nu0 = n0>R?(u0*R/n0):u0,
47340                 nv0 = n0>R?(v0*R/n0):v0,
47341                 nw0 = (float)std::sqrt(std::max(0.0f,R2 - nu0*nu0 - nv0*nv0)),
47342                 nu1 = n1>R?(u1*R/n1):u1,
47343                 nv1 = n1>R?(v1*R/n1):v1,
47344                 nw1 = (float)std::sqrt(std::max(0.0f,R2 - nu1*nu1 - nv1*nv1)),
47345                 u = nv0*nw1 - nw0*nv1,
47346                 v = nw0*nu1 - nu0*nw1,
47347                 w = nv0*nu1 - nu0*nv1,
47348                 n = cimg::hypot(u,v,w),
47349                 alpha = (float)std::asin(n/R2)*180/cimg::PI;
47350               pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2));
47351               view3d.assign();
47352             } else if (disp.button()&2 && pose3d && oY3d!=Y3d) {  // Right button: zoom.
47353               pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign();
47354             }
47355             if (disp.wheel()) { // Wheel: zoom
47356               pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
47357             }
47358             if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift.
47359               pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
47360             }
47361             oX3d = X3d; oY3d = Y3d;
47362           }
47363           mx = my = -1; X = Y = Z = -1;
47364           break;
47365         }
47366 
47367         if (phase) {
47368           if (!feature_type) shape_selected = phase?true:false;
47369           else {
47370             if (_depth>1) shape_selected = (phase==3)?true:false;
47371             else shape_selected = (phase==2)?true:false;
47372           }
47373         }
47374 
47375         if (X0<0) X0 = 0;
47376         if (X0>=width()) X0 = width() - 1;
47377         if (Y0<0) Y0 = 0;
47378         if (Y0>=height()) Y0 = height() - 1;
47379         if (Z0<0) Z0 = 0;
47380         if (Z0>=depth()) Z0 = depth() - 1;
47381         if (X1<1) X1 = 0;
47382         if (X1>=width()) X1 = width() - 1;
47383         if (Y1<0) Y1 = 0;
47384         if (Y1>=height()) Y1 = height() - 1;
47385         if (Z1<0) Z1 = 0;
47386         if (Z1>=depth()) Z1 = depth() - 1;
47387 
47388         // Draw visualization image on the display
47389         if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) {
47390 
47391           if (!visu0) { // Create image of projected planes.
47392             if (thumb) thumb.__get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
47393             else __get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
47394             visu0.resize(disp);
47395             view3d.assign();
47396             points3d.assign();
47397           }
47398 
47399           if (is_view3d && _depth>1 && !view3d) { // Create 3d view for volumetric images.
47400             const unsigned int
47401               _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1),
47402               _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1),
47403               x3d = _x3d>=visu0._width?visu0._width - 1:_x3d,
47404               y3d = _y3d>=visu0._height?visu0._height - 1:_y3d;
47405             CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3).
47406               move_to(view3d);
47407             if (!points3d) {
47408               get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
47409               points3d.append(CImg<floatT>(8,3,1,1,
47410                                            0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0,
47411                                            0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1,
47412                                            0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x');
47413               CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
47414               CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
47415               CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
47416               CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
47417               CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
47418               CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
47419               colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
47420               opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
47421               if (!phase) {
47422                 opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
47423                 sel_primitives3d.assign();
47424                 sel_colors3d.assign();
47425                 sel_opacities3d.assign();
47426               } else {
47427                 if (feature_type==2) {
47428                   points3d.append(CImg<floatT>(8,3,1,1,
47429                                                X0,X1,X1,X0,X0,X1,X1,X0,
47430                                                Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
47431                                                Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
47432                   sel_primitives3d.assign();
47433                   CImg<uintT>::vector(20,21).move_to(sel_primitives3d);
47434                   CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
47435                   CImg<uintT>::vector(22,23).move_to(sel_primitives3d);
47436                   CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
47437                   CImg<uintT>::vector(24,25).move_to(sel_primitives3d);
47438                   CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
47439                   CImg<uintT>::vector(26,27).move_to(sel_primitives3d);
47440                   CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
47441                   CImg<uintT>::vector(20,24).move_to(sel_primitives3d);
47442                   CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
47443                   CImg<uintT>::vector(22,26).move_to(sel_primitives3d);
47444                   CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
47445                 } else {
47446                   points3d.append(CImg<floatT>(2,3,1,1,
47447                                                X0,X1,
47448                                                Y0,Y1,
47449                                                Z0,Z1),'x');
47450                   sel_primitives3d.assign(CImg<uintT>::vector(20,21));
47451                 }
47452                 sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
47453                 sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
47454               }
47455               points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d();
47456               points3d*=0.75f*std::min(view3d._width,view3d._height);
47457             }
47458 
47459             if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
47460             CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
47461             const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
47462             if (sel_primitives3d)
47463               view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
47464                                    pose3d(3,1) + 0.5f*view3d._height,
47465                                    pose3d(3,2),
47466                                    rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
47467                                    2,true,500,0,0,0,0,0,zbuffer3d);
47468             view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
47469                                  pose3d(3,1) + 0.5f*view3d._height,
47470                                  pose3d(3,2),
47471                                  rotated_points3d,primitives3d,colors3d,opacities3d,
47472                                  2,true,500,0,0,0,0,0,zbuffer3d);
47473             visu0.draw_image(x3d,y3d,view3d);
47474           }
47475           visu = visu0;
47476 
47477           if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
47478           else {
47479             if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }}
47480             else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
47481             const int d = (depth()>1)?depth():0;
47482             int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z;
47483             if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; }
47484             int
47485               w = disp.width(), W = width() + d,
47486               h = disp.height(), H = height() + d,
47487               _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX),
47488               _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY),
47489               _xn = (int)((_vX + 1.0f)*w/W - 1), xn = _xn + ((int)((_xn + 1.0f)*W/w)!=_vX + 1),
47490               _yn = (int)((_vY + 1.0f)*h/H - 1), yn = _yn + ((int)((_yn + 1.0f)*H/h)!=_vY + 1),
47491               _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()),
47492               _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()),
47493               _zxn = (int)((_vZ + width() + 1.0f)*w/W - 1),
47494                        zxn = _zxn + ((int)((_zxn + 1.0f)*W/w)!=_vZ + width() + 1),
47495               _zyn = (int)((_vZ + height() + 1.0f)*h/H - 1),
47496                        zyn = _zyn + ((int)((_zyn + 1.0f)*H/h)!=_vZ + height() + 1),
47497               _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.0f)*W/w)!=width()),
47498               _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.0f)*H/h)!=height()),
47499               xc = (xp + xn)/2,
47500               yc = (yp + yn)/2,
47501               zxc = (zxp + zxn)/2,
47502               zyc = (zyp + zyn)/2,
47503               xf = (int)(X*w/W),
47504               yf = (int)(Y*h/H),
47505               zxf = (int)((Z + width())*w/W),
47506               zyf = (int)((Z + height())*h/H);
47507 
47508             if (is_axes) { // Draw axes.
47509               visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00).
47510                 draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF).
47511                 draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00).
47512                 draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF);
47513               if (_depth>1)
47514                 visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00).
47515                   draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF).
47516                   draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00).
47517                   draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF);
47518             }
47519 
47520             // Draw box cursor.
47521             if (xn - xp>=4 && yn - yp>=4)
47522               visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f).
47523                 draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA).
47524                 draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555);
47525             if (_depth>1) {
47526               if (yn - yp>=4 && zxn - zxp>=4)
47527                 visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f).
47528                                               draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA).
47529                                               draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555);
47530               if (xn - xp>=4 && zyn - zyp>=4)
47531                 visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f).
47532                           draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA).
47533                           draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555);
47534             }
47535 
47536             // Draw selection.
47537             if (phase && (phase!=1 || area_started==area)) {
47538               const int
47539                 _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0),
47540                 _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0),
47541                 _xn0 = (int)((X0 + 1.0f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.0f)*W/w)!=X0 + 1),
47542                 _yn0 = (int)((Y0 + 1.0f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.0f)*H/h)!=Y0 + 1),
47543                 _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()),
47544                 _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()),
47545                 _zxn0 = (int)((Z0 + width() + 1.0f)*w/W - 1),
47546                 zxn0 = _zxn0 + ((int)((_zxn0 + 1.0f)*W/w)!=Z0 + width() + 1),
47547                 _zyn0 = (int)((Z0 + height() + 1.0f)*h/H - 1),
47548                 zyn0 = _zyn0 + ((int)((_zyn0 + 1.0f)*H/h)!=Z0 + height() + 1),
47549                 xc0 = (xp0 + xn0)/2,
47550                 yc0 = (yp0 + yn0)/2,
47551                 zxc0 = (zxp0 + zxn0)/2,
47552                 zyc0 = (zyp0 + zyn0)/2;
47553 
47554               switch (feature_type) {
47555               case 1 : {
47556                 visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x55555555).
47557                   draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA);
47558                 if (d) {
47559                   visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x55555555).
47560                     draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xAAAAAAAA).
47561                     draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x55555555).
47562                     draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xAAAAAAAA);
47563                 }
47564               } break;
47565               case 2 : {
47566                 visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f).
47567                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555).
47568                   draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA).
47569                   draw_arrow(xc0,yc0,xc,yc,background_color,0.5f,30,5,0x55555555).
47570                   draw_arrow(xc0,yc0,xc,yc,foreground_color,0.5f,30,5,0xAAAAAAAA);
47571                 if (d) {
47572                   visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f).
47573                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
47574                                    background_color,0.9f,0x55555555).
47575                     draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
47576                                    foreground_color,0.9f,0xAAAAAAAA).
47577                     draw_arrow(zxc0,yc0,zxc,yc,background_color,0.5f,30,5,0x55555555).
47578                     draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.5f,30,5,0xAAAAAAAA).
47579                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
47580                                    background_color,0.2f).
47581                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
47582                                    background_color,0.9f,0x55555555).
47583                     draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
47584                                    foreground_color,0.9f,0xAAAAAAAA).
47585                     draw_arrow(xp0,zyp0,xn,zyn,background_color,0.5f,30,5,0x55555555).
47586                     draw_arrow(xp0,zyp0,xn,zyn,foreground_color,0.5f,30,5,0xAAAAAAAA);
47587                 }
47588               } break;
47589               case 3 : {
47590                 visu.draw_ellipse(xc0,yc0,
47591                                   (float)cimg::abs(xc - xc0),
47592                                   (float)cimg::abs(yc - yc0),0,background_color,0.2f).
47593                   draw_ellipse(xc0,yc0,
47594                                (float)cimg::abs(xc - xc0),
47595                                (float)cimg::abs(yc - yc0),0,foreground_color,0.9f,~0U).
47596                   draw_point(xc0,yc0,foreground_color,0.9f);
47597                 if (d) {
47598                   visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
47599                                     background_color,0.2f).
47600                     draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
47601                                  foreground_color,0.9f,~0U).
47602                     draw_point(zxc0,yc0,foreground_color,0.9f).
47603                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
47604                                  background_color,0.2f).
47605                     draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
47606                                  foreground_color,0.9f,~0U).
47607                     draw_point(xc0,zyc0,foreground_color,0.9f);
47608                 }
47609               } break;
47610               }
47611             }
47612 
47613             // Draw text info.
47614             if (my>=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false;
47615             if (!feature_type || !phase) {
47616               if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
47617                 if (_depth>1 || force_display_z_coord)
47618                   cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z);
47619                 else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y);
47620                 CImg<T> values = get_vector_at((int)X,(int)Y,(int)Z);
47621                 const bool is_large_spectrum = values._height>16;
47622                 if (is_large_spectrum)
47623                   values.draw_image(0,8,values.get_rows(values._height - 8,values._height - 1)).resize(1,16,1,1,0);
47624                 char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512;
47625                 for (unsigned int c = 0; c<values._height && ctext<ltext; ++c) {
47626                   cimg_snprintf(ctext,24,cimg::type<T>::format_s(),
47627                                 cimg::type<T>::format(values[c]));
47628                   ctext += std::strlen(ctext);
47629                   if (c==7 && is_large_spectrum) {
47630                     cimg_snprintf(ctext,24," (...)");
47631                     ctext += std::strlen(ctext);
47632                   }
47633                   *(ctext++) = ' '; *ctext = 0;
47634                 }
47635                 std::strcpy(text._data + std::strlen(text),"] ");
47636               }
47637             } else switch (feature_type) {
47638               case 1 : {
47639                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
47640                   length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
47641                 if (_depth>1 || force_display_z_coord)
47642                   cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ",
47643                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length);
47644                 else cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ",
47645                                    origX + X0,origY + Y0,origX + X1,origY + Y1,length,
47646                                    cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
47647               } break;
47648               case 2 : {
47649                 const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
47650                   length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
47651                 if (_depth>1 || force_display_z_coord)
47652                   cimg_snprintf(text,text._width,
47653                                 " Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d), Length = %g ",
47654                                 origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),origZ + (Z0<Z1?Z0:Z1),
47655                                 origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),origZ + (Z0<Z1?Z1:Z0),
47656                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1),length);
47657                 else cimg_snprintf(text,text._width,
47658                                    " Box (%d,%d)-(%d,%d), Size = (%d,%d), Length = %g, Angle = %g\260 ",
47659                                    origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
47660                                    origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
47661                                    1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length,
47662                                    cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
47663               } break;
47664               default :
47665                 if (_depth>1 || force_display_z_coord)
47666                   cimg_snprintf(text,text._width," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ",
47667                                 origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,
47668                                 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1));
47669                 else cimg_snprintf(text,text._width," Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d) ",
47670                                    origX + X0,origY + Y0,origX + X1,origY + Y1,
47671                                    1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1));
47672               }
47673             if (phase || (mx>=0 && my>=0))
47674               visu.draw_text(0,text_down?visu.height() - 13:0,text,foreground_color,background_color,0.7f,13);
47675           }
47676 
47677           disp.display(visu);
47678         }
47679         if (!shape_selected) disp.wait();
47680         if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
47681         omx = mx; omy = my;
47682         if (!exit_on_anykey && key && key!=cimg::keyESC &&
47683             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
47684           key = 0;
47685         }
47686       }
47687 
47688       // Return result.
47689       CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
47690       if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
47691       if (shape_selected) {
47692         if (feature_type==2) {
47693           if (is_deep_selection) switch (area_started) {
47694             case 1 : Z0 = 0; Z1 = _depth - 1; break;
47695             case 2 : Y0 = 0; Y1 = _height - 1; break;
47696             case 3 : X0 = 0; X1 = _width - 1; break;
47697           }
47698           if (X0>X1) cimg::swap(X0,X1);
47699           if (Y0>Y1) cimg::swap(Y0,Y1);
47700           if (Z0>Z1) cimg::swap(Z0,Z1);
47701         }
47702 	if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
47703 	switch (feature_type) {
47704 	case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
47705         case 3 :
47706           res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0);
47707           res[0] = X0; res[1] = Y0; res[2] = Z0;
47708           break;
47709 	default : res[0] = X0; res[1] = Y0; res[2] = Z0;
47710 	}
47711       }
47712       if (!exit_on_anykey || !(disp.button()&4)) disp.set_button();
47713       if (!visible_cursor) disp.show_mouse();
47714       disp._normalization = old_normalization;
47715       disp._is_resized = old_is_resized;
47716       if (key!=~0U) disp.set_key(key);
47717       return res;
47718     }
47719 
47720     // Return a visualizable uchar8 image for display routines.
47721     CImg<ucharT> __get_select(const CImgDisplay& disp, const int normalization,
47722                               const int x, const int y, const int z) const {
47723       if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
47724       const CImg<T> crop = get_shared_channels(0,std::min(2,spectrum() - 1));
47725       CImg<Tuchar> img2d;
47726       if (_depth>1) {
47727         const int mdisp = std::min(disp.screen_width(),disp.screen_height());
47728         if (depth()>mdisp) {
47729           crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d);
47730           img2d.projections2d(x,y,z*img2d._depth/_depth);
47731         } else crop.get_projections2d(x,y,z).move_to(img2d);
47732       } else CImg<Tuchar>(crop,false).move_to(img2d);
47733 
47734       // Check for inf and NaN values.
47735       if (cimg::type<T>::is_float() && normalization) {
47736         bool is_inf = false, is_nan = false;
47737         cimg_for(img2d,ptr,Tuchar)
47738           if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; }
47739           else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; }
47740         if (is_inf || is_nan) {
47741           Tint m0 = (Tint)cimg::type<T>::max(), M0 = (Tint)cimg::type<T>::min();
47742           if (!normalization) { m0 = 0; M0 = 255; }
47743           else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; }
47744           else
47745             cimg_for(img2d,ptr,Tuchar)
47746               if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) {
47747                 if (*ptr<(Tuchar)m0) m0 = *ptr;
47748                 if (*ptr>(Tuchar)M0) M0 = *ptr;
47749               }
47750           const T
47751             val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0),
47752             val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0);
47753           if (is_nan)
47754             cimg_for(img2d,ptr,Tuchar)
47755               if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values.
47756           if (is_inf)
47757             cimg_for(img2d,ptr,Tuchar)
47758               if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values.
47759         }
47760       }
47761 
47762       switch (normalization) {
47763       case 1 : img2d.normalize((ucharT)0,(ucharT)255); break;
47764       case 2 : {
47765         const float m = disp._min, M = disp._max;
47766         (img2d-=m)*=255.0f/(M - m>0?M - m:1);
47767       } break;
47768       case 3 :
47769         if (cimg::type<T>::is_float()) img2d.normalize((ucharT)0,(ucharT)255);
47770         else {
47771           const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::max();
47772           (img2d-=m)*=255.0f/(M - m>0?M - m:1);
47773         } break;
47774       }
47775       if (img2d.spectrum()==2) img2d.channels(0,2);
47776       return img2d;
47777     }
47778 
47779     //! Select sub-graph in a graph.
47780     CImg<intT> get_select_graph(CImgDisplay &disp,
47781                                 const unsigned int plot_type=1, const unsigned int vertex_type=1,
47782                                 const char *const labelx=0, const double xmin=0, const double xmax=0,
47783                                 const char *const labely=0, const double ymin=0, const double ymax=0,
47784                                 const bool exit_on_anykey=false) const {
47785       if (is_empty())
47786         throw CImgInstanceException(_cimg_instance
47787                                     "select_graph(): Empty instance.",
47788                                     cimg_instance);
47789       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
47790                    set_title("CImg<%s>",pixel_type());
47791       const ulongT siz = (ulongT)_width*_height*_depth;
47792       const unsigned int old_normalization = disp.normalization();
47793       disp.show().set_button().set_wheel()._normalization = 0;
47794 
47795       double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
47796       if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
47797       if (nymin==nymax) { --nymin; ++nymax; }
47798       if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; }
47799 
47800       static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
47801       static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
47802       static unsigned int odimv = 0;
47803       static CImg<ucharT> colormap;
47804       if (odimv!=_spectrum) {
47805         odimv = _spectrum;
47806         colormap = CImg<ucharT>(3,_spectrum,1,1,120).noise(70,1);
47807         if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
47808         else {
47809           colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
47810           if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; }
47811           if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; }
47812         }
47813       }
47814 
47815       CImg<ucharT> visu0, visu, graph, text, axes;
47816       int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
47817       const unsigned int one = plot_type==3?0U:1U;
47818       unsigned int okey = 0, obutton = 0;
47819       CImg<charT> message(1024);
47820       CImg_3x3(I,unsigned char);
47821 
47822       for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
47823         const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
47824         const unsigned int key = disp.key(), button = disp.button();
47825 
47826         // Generate graph representation.
47827         if (!visu0) {
47828           visu0.assign(disp.width(),disp.height(),1,3,220);
47829           const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
47830           if (gdimx>0 && gdimy>0) {
47831             graph.assign(gdimx,gdimy,1,3,255);
47832             if (siz<32) {
47833               if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
47834                                          false,true,black,0.2f,0x33333333,0x33333333);
47835             } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
47836             cimg_forC(*this,c)
47837               graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
47838                                plot_type,vertex_type,nymax,nymin);
47839 
47840             axes.assign(gdimx,gdimy,1,1,0);
47841             const float
47842               dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin),
47843               px = (float)std::pow(10.0,(int)std::log10(dx?dx:1) - 2.0),
47844               py = (float)std::pow(10.0,(int)std::log10(dy?dy:1) - 2.0);
47845             const CImg<Tdouble>
47846               seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
47847                 CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz).round(px),
47848               seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin).round(py);
47849 
47850             const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
47851             axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero);
47852             if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero);
47853             if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero);
47854 	    if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero);
47855 	    if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero);
47856 
47857             cimg_for3x3(axes,x,y,0,0,I,unsigned char)
47858               if (Icc) {
47859                 if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
47860                 else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
47861               }
47862               else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
47863                 cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3);
47864 
47865             visu0.draw_image(16,16,graph);
47866 	    visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2).
47867 	      draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white);
47868           } else graph.assign();
47869           text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
47870           visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text);
47871           text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
47872           visu0.draw_image(1,(visu0.height() - text.height())/2,~text);
47873           visu.assign();
47874         }
47875 
47876         // Generate and display current view.
47877         if (!visu) {
47878           visu.assign(visu0);
47879           if (graph && x0>=0 && x1>=0) {
47880             const int
47881               nx0 = x0<=x1?x0:x1,
47882               nx1 = x0<=x1?x1:x0,
47883               ny0 = y0<=y1?y0:y1,
47884               ny1 = y0<=y1?y1:y0,
47885               sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
47886               sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
47887               sy0 = 16 + ny0,
47888               sy1 = 16 + ny1;
47889             if (y0>=0 && y1>=0)
47890               visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
47891             else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f).
47892                    draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU).
47893                    draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU);
47894           }
47895           if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) {
47896             if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U);
47897             const unsigned int
47898               x = (unsigned int)cimg::round((mouse_x - 16.0f)*(siz - one)/(disp.width() - 32),1,one?0:-1);
47899             const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1);
47900             if (_spectrum>=7)
47901               cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
47902                             (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
47903                             (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3),
47904                             (double)(*this)(x,0,0,_spectrum - 1));
47905             else {
47906               cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx);
47907               cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
47908               cimg_sprintf(message._data + std::strlen(message),")");
47909             }
47910 	    if (x0>=0 && x1>=0) {
47911 	      const unsigned int
47912                 nx0 = (unsigned int)(x0<=x1?x0:x1),
47913                 nx1 = (unsigned int)(x0<=x1?x1:x0),
47914                 ny0 = (unsigned int)(y0<=y1?y0:y1),
47915                 ny1 = (unsigned int)(y0<=y1?y1:y0);
47916 	      const double
47917                 cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
47918                 cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
47919                 cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32),
47920                 cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32);
47921 	      if (y0>=0 && y1>=0)
47922 	        cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
47923                              x0,cx0,cy0,x1 + one,cx1,cy1);
47924 	      else
47925 	        cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]",
47926                              x0,cx0,x1 + one,cx1);
47927 	    }
47928             text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
47929             visu.draw_image((visu.width() - text.width())/2,1,~text);
47930           }
47931           visu.display(disp);
47932         }
47933 
47934         // Test keys.
47935         CImg<charT> filename(32);
47936         switch (okey = key) {
47937 #if cimg_OS!=2
47938         case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
47939 #endif
47940         case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
47941         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47942           disp.set_fullscreen(false).
47943             resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
47944                    CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
47945             _is_resized = true;
47946           disp.set_key(key,false); okey = 0;
47947         } break;
47948         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47949           disp.set_fullscreen(false).
47950             resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
47951           disp.set_key(key,false); okey = 0;
47952         } break;
47953         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47954             disp.set_fullscreen(false).
47955               resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
47956                                     CImgDisplay::screen_height()/2,1),false)._is_resized = true;
47957             disp.set_key(key,false); okey = 0;
47958           } break;
47959         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47960             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
47961             disp.set_key(key,false); okey = 0;
47962           } break;
47963         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47964             static unsigned int snap_number = 0;
47965             if (visu || visu0) {
47966               CImg<ucharT> &screen = visu?visu:visu0;
47967               std::FILE *file;
47968               do {
47969                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
47970                 if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
47971               } while (file);
47972               (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp);
47973               screen.save(filename);
47974               (+screen).draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename._data).display(disp);
47975             }
47976             disp.set_key(key,false); okey = 0;
47977           } break;
47978         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
47979             static unsigned int snap_number = 0;
47980             if (visu || visu0) {
47981               CImg<ucharT> &screen = visu?visu:visu0;
47982               std::FILE *file;
47983               do {
47984 #ifdef cimg_use_zlib
47985                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
47986 #else
47987                 cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
47988 #endif
47989                 if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
47990               } while (file);
47991               (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp);
47992               save(filename);
47993               (+screen).draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename._data).display(disp);
47994             }
47995             disp.set_key(key,false); okey = 0;
47996           } break;
47997         }
47998 
47999         // Handle mouse motion and mouse buttons
48000         if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
48001           visu.assign();
48002           if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
48003             const int
48004               mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32),
48005               cx = cimg::cut(mx,0,(int)(siz - 1 - one)),
48006               my = mouse_y - 16,
48007               cy = cimg::cut(my,0,disp.height() - 32);
48008 	    if (button&1) {
48009               if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
48010             }
48011 	    else if (button&2) {
48012               if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
48013             }
48014             else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
48015           } else if (!button && obutton) selected = true;
48016           obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
48017         }
48018         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
48019         if (visu && visu0) disp.wait();
48020         if (!exit_on_anykey && okey && okey!=cimg::keyESC &&
48021             (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
48022           disp.set_key(key,false);
48023           okey = 0;
48024         }
48025       }
48026 
48027       disp._normalization = old_normalization;
48028       if (x1>=0 && x1<x0) cimg::swap(x0,x1);
48029       if (y1<y0) cimg::swap(y0,y1);
48030       disp.set_key(okey);
48031       return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1);
48032     }
48033 
48034     //! Load image from a file.
48035     /**
48036        \param filename Filename, as a C-string.
48037        \note The extension of \c filename defines the file format. If no filename
48038        extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file.
48039     **/
48040     CImg<T>& load(const char *const filename) {
48041       if (!filename)
48042         throw CImgArgumentException(_cimg_instance
48043                                     "load(): Specified filename is (null).",
48044                                     cimg_instance);
48045 
48046       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
48047         CImg<charT> filename_local(256);
48048         load(cimg::load_network(filename,filename_local));
48049         std::remove(filename_local);
48050         return *this;
48051       }
48052 
48053       const char *const ext = cimg::split_filename(filename);
48054       const unsigned int omode = cimg::exception_mode();
48055       cimg::exception_mode(0);
48056       bool is_loaded = true;
48057       try {
48058 #ifdef cimg_load_plugin
48059         cimg_load_plugin(filename);
48060 #endif
48061 #ifdef cimg_load_plugin1
48062         cimg_load_plugin1(filename);
48063 #endif
48064 #ifdef cimg_load_plugin2
48065         cimg_load_plugin2(filename);
48066 #endif
48067 #ifdef cimg_load_plugin3
48068         cimg_load_plugin3(filename);
48069 #endif
48070 #ifdef cimg_load_plugin4
48071         cimg_load_plugin4(filename);
48072 #endif
48073 #ifdef cimg_load_plugin5
48074         cimg_load_plugin5(filename);
48075 #endif
48076 #ifdef cimg_load_plugin6
48077         cimg_load_plugin6(filename);
48078 #endif
48079 #ifdef cimg_load_plugin7
48080         cimg_load_plugin7(filename);
48081 #endif
48082 #ifdef cimg_load_plugin8
48083         cimg_load_plugin8(filename);
48084 #endif
48085         // Ascii formats
48086         if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
48087         else if (!cimg::strcasecmp(ext,"dlm") ||
48088                  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
48089 
48090         // 2d binary formats
48091         else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
48092         else if (!cimg::strcasecmp(ext,"jpg") ||
48093                  !cimg::strcasecmp(ext,"jpeg") ||
48094                  !cimg::strcasecmp(ext,"jpe") ||
48095                  !cimg::strcasecmp(ext,"jfif") ||
48096                  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
48097         else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
48098         else if (!cimg::strcasecmp(ext,"ppm") ||
48099                  !cimg::strcasecmp(ext,"pgm") ||
48100                  !cimg::strcasecmp(ext,"pnm") ||
48101                  !cimg::strcasecmp(ext,"pbm") ||
48102                  !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
48103         else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
48104         else if (!cimg::strcasecmp(ext,"tif") ||
48105                  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
48106         else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
48107         else if (!cimg::strcasecmp(ext,"cr2") ||
48108                  !cimg::strcasecmp(ext,"crw") ||
48109                  !cimg::strcasecmp(ext,"dcr") ||
48110                  !cimg::strcasecmp(ext,"mrw") ||
48111                  !cimg::strcasecmp(ext,"nef") ||
48112                  !cimg::strcasecmp(ext,"orf") ||
48113                  !cimg::strcasecmp(ext,"pix") ||
48114                  !cimg::strcasecmp(ext,"ptx") ||
48115                  !cimg::strcasecmp(ext,"raf") ||
48116                  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
48117         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
48118 
48119         // 3d binary formats
48120         else if (!cimg::strcasecmp(ext,"dcm") ||
48121                  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
48122         else if (!cimg::strcasecmp(ext,"hdr") ||
48123                  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
48124         else if (!cimg::strcasecmp(ext,"par") ||
48125                  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
48126         else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
48127         else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
48128         else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
48129         else if (!cimg::strcasecmp(ext,"cimg") ||
48130                  !cimg::strcasecmp(ext,"cimgz") ||
48131                  !*ext)  return load_cimg(filename);
48132 
48133         // Archive files
48134         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
48135 
48136         // Image sequences
48137         else if (!cimg::strcasecmp(ext,"avi") ||
48138                  !cimg::strcasecmp(ext,"mov") ||
48139                  !cimg::strcasecmp(ext,"asf") ||
48140                  !cimg::strcasecmp(ext,"divx") ||
48141                  !cimg::strcasecmp(ext,"flv") ||
48142                  !cimg::strcasecmp(ext,"mpg") ||
48143                  !cimg::strcasecmp(ext,"m1v") ||
48144                  !cimg::strcasecmp(ext,"m2v") ||
48145                  !cimg::strcasecmp(ext,"m4v") ||
48146                  !cimg::strcasecmp(ext,"mjp") ||
48147                  !cimg::strcasecmp(ext,"mp4") ||
48148                  !cimg::strcasecmp(ext,"mkv") ||
48149                  !cimg::strcasecmp(ext,"mpe") ||
48150                  !cimg::strcasecmp(ext,"movie") ||
48151                  !cimg::strcasecmp(ext,"ogm") ||
48152                  !cimg::strcasecmp(ext,"ogg") ||
48153                  !cimg::strcasecmp(ext,"ogv") ||
48154                  !cimg::strcasecmp(ext,"qt") ||
48155                  !cimg::strcasecmp(ext,"rm") ||
48156                  !cimg::strcasecmp(ext,"vob") ||
48157                  !cimg::strcasecmp(ext,"wmv") ||
48158                  !cimg::strcasecmp(ext,"xvid") ||
48159                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
48160         else is_loaded = false;
48161       } catch (CImgIOException&) { is_loaded = false; }
48162 
48163       // If nothing loaded, try to guess file format from magic number in file.
48164       if (!is_loaded) {
48165         std::FILE *file = std_fopen(filename,"rb");
48166         if (!file) {
48167           cimg::exception_mode(omode);
48168           throw CImgIOException(_cimg_instance
48169                                 "load(): Failed to open file '%s'.",
48170                                 cimg_instance,
48171                                 filename);
48172         }
48173 
48174         const char *const f_type = cimg::ftype(file,filename);
48175         std::fclose(file);
48176         is_loaded = true;
48177         try {
48178           if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
48179           else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
48180           else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
48181           else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
48182           else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
48183           else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
48184           else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
48185           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
48186           else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
48187           else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
48188           else is_loaded = false;
48189         } catch (CImgIOException&) { is_loaded = false; }
48190       }
48191 
48192       // If nothing loaded, try to load file with other means.
48193       if (!is_loaded) {
48194         try {
48195           load_other(filename);
48196         } catch (CImgIOException&) {
48197           cimg::exception_mode(omode);
48198           throw CImgIOException(_cimg_instance
48199                                 "load(): Failed to recognize format of file '%s'.",
48200                                 cimg_instance,
48201                                 filename);
48202         }
48203       }
48204       cimg::exception_mode(omode);
48205       return *this;
48206     }
48207 
48208     //! Load image from a file \newinstance.
48209     static CImg<T> get_load(const char *const filename) {
48210       return CImg<T>().load(filename);
48211     }
48212 
48213     //! Load image from an ascii file.
48214     /**
48215        \param filename Filename, as a C -string.
48216     **/
48217     CImg<T>& load_ascii(const char *const filename) {
48218       return _load_ascii(0,filename);
48219     }
48220 
48221     //! Load image from an ascii file \inplace.
48222     static CImg<T> get_load_ascii(const char *const filename) {
48223       return CImg<T>().load_ascii(filename);
48224     }
48225 
48226     //! Load image from an ascii file \overloading.
48227     CImg<T>& load_ascii(std::FILE *const file) {
48228       return _load_ascii(file,0);
48229     }
48230 
48231     //! Loadimage from an ascii file \newinstance.
48232     static CImg<T> get_load_ascii(std::FILE *const file) {
48233       return CImg<T>().load_ascii(file);
48234     }
48235 
48236     CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
48237       if (!file && !filename)
48238         throw CImgArgumentException(_cimg_instance
48239                                     "load_ascii(): Specified filename is (null).",
48240                                     cimg_instance);
48241 
48242       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
48243       CImg<charT> line(256); *line = 0;
48244       int err = std::fscanf(nfile,"%255[^\n]",line._data);
48245       unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
48246       cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
48247       err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]");
48248       if (!dx || !dy || !dz || !dc) {
48249         if (!file) cimg::fclose(nfile);
48250         throw CImgIOException(_cimg_instance
48251                               "load_ascii(): Invalid ascii header in file '%s', image dimensions are set "
48252                               "to (%u,%u,%u,%u).",
48253                               cimg_instance,
48254                               filename?filename:"(FILE*)",dx,dy,dz,dc);
48255       }
48256       assign(dx,dy,dz,dc);
48257       const ulongT siz = size();
48258       ulongT off = 0;
48259       double val;
48260       T *ptr = _data;
48261       for (err = 1, off = 0; off<siz && err==1; ++off) {
48262         err = std::fscanf(nfile,"%lf%*[^0-9.eEinfa+-]",&val);
48263         *(ptr++) = (T)val;
48264       }
48265       if (err!=1)
48266         cimg::warn(_cimg_instance
48267                    "load_ascii(): Only %lu/%lu values read from file '%s'.",
48268                    cimg_instance,
48269                    off - 1,siz,filename?filename:"(FILE*)");
48270 
48271       if (!file) cimg::fclose(nfile);
48272       return *this;
48273     }
48274 
48275     //! Load image from a DLM file.
48276     /**
48277       \param filename Filename, as a C-string.
48278     **/
48279     CImg<T>& load_dlm(const char *const filename) {
48280       return _load_dlm(0,filename);
48281     }
48282 
48283     //! Load image from a DLM file \newinstance.
48284     static CImg<T> get_load_dlm(const char *const filename) {
48285       return CImg<T>().load_dlm(filename);
48286     }
48287 
48288     //! Load image from a DLM file \overloading.
48289     CImg<T>& load_dlm(std::FILE *const file) {
48290       return _load_dlm(file,0);
48291     }
48292 
48293     //! Load image from a DLM file \newinstance.
48294     static CImg<T> get_load_dlm(std::FILE *const file) {
48295       return CImg<T>().load_dlm(file);
48296     }
48297 
48298     CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
48299       if (!file && !filename)
48300         throw CImgArgumentException(_cimg_instance
48301                                     "load_dlm(): Specified filename is (null).",
48302                                     cimg_instance);
48303 
48304       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
48305       CImg<charT> delimiter(256), tmp(256); *delimiter = *tmp = 0;
48306       unsigned int cdx = 0, dx = 0, dy = 0;
48307       int err = 0;
48308       double val;
48309       assign(256,256,1,1,(T)0);
48310       while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) {
48311         if (err>0) (*this)(cdx++,dy) = (T)val;
48312         if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
48313         char c = 0;
48314         if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') {
48315           dx = std::max(cdx,dx);
48316           if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
48317           cdx = 0;
48318         }
48319       }
48320       if (cdx && err==1) { dx = cdx; ++dy; }
48321       if (!dx || !dy) {
48322         if (!file) cimg::fclose(nfile);
48323         throw CImgIOException(_cimg_instance
48324                               "load_dlm(): Invalid DLM file '%s'.",
48325                               cimg_instance,
48326                               filename?filename:"(FILE*)");
48327       }
48328       resize(dx,dy,1,1,0);
48329       if (!file) cimg::fclose(nfile);
48330       return *this;
48331     }
48332 
48333     //! Load image from a BMP file.
48334     /**
48335        \param filename Filename, as a C-string.
48336     **/
48337     CImg<T>& load_bmp(const char *const filename) {
48338       return _load_bmp(0,filename);
48339     }
48340 
48341     //! Load image from a BMP file \newinstance.
48342     static CImg<T> get_load_bmp(const char *const filename) {
48343       return CImg<T>().load_bmp(filename);
48344     }
48345 
48346     //! Load image from a BMP file \overloading.
48347     CImg<T>& load_bmp(std::FILE *const file) {
48348       return _load_bmp(file,0);
48349     }
48350 
48351     //! Load image from a BMP file \newinstance.
48352     static CImg<T> get_load_bmp(std::FILE *const file) {
48353       return CImg<T>().load_bmp(file);
48354     }
48355 
48356     CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
48357       if (!file && !filename)
48358         throw CImgArgumentException(_cimg_instance
48359                                     "load_bmp(): Specified filename is (null).",
48360                                     cimg_instance);
48361 
48362       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
48363       CImg<ucharT> header(54);
48364       cimg::fread(header._data,54,nfile);
48365       if (*header!='B' || header[1]!='M') {
48366         if (!file) cimg::fclose(nfile);
48367         throw CImgIOException(_cimg_instance
48368                               "load_bmp(): Invalid BMP file '%s'.",
48369                               cimg_instance,
48370                               filename?filename:"(FILE*)");
48371       }
48372 
48373       // Read header and pixel buffer
48374       int
48375         file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
48376         offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
48377         header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24),
48378         dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
48379         dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
48380         compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
48381         nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
48382         bpp = header[0x1C] + (header[0x1D]<<8);
48383 
48384       if (!file_size || file_size==offset) {
48385         cimg::fseek(nfile,0,SEEK_END);
48386         file_size = (int)cimg::ftell(nfile);
48387         cimg::fseek(nfile,54,SEEK_SET);
48388       }
48389       if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR);
48390 
48391       const int
48392         dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)),
48393         align_bytes = (4 - dx_bytes%4)%4;
48394       const ulongT
48395         cimg_iobuffer = (ulongT)24*1024*1024,
48396         buf_size = std::min((ulongT)cimg::abs(dy)*(dx_bytes + align_bytes),(ulongT)file_size - offset);
48397 
48398       CImg<intT> colormap;
48399       if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
48400       if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
48401       const int xoffset = offset - 14 - header_size - 4*nb_colors;
48402       if (xoffset>0) cimg::fseek(nfile,xoffset,SEEK_CUR);
48403 
48404       CImg<ucharT> buffer;
48405       if (buf_size<cimg_iobuffer) {
48406         buffer.assign(cimg::abs(dy)*(dx_bytes + align_bytes),1,1,1,0);
48407         cimg::fread(buffer._data,buf_size,nfile);
48408       } else buffer.assign(dx_bytes + align_bytes);
48409       unsigned char *ptrs = buffer;
48410 
48411       // Decompress buffer (if necessary)
48412       if (compression) {
48413         if (file)
48414           throw CImgIOException(_cimg_instance
48415                                 "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
48416                                 cimg_instance);
48417         else {
48418           if (!file) cimg::fclose(nfile);
48419           return load_other(filename);
48420         }
48421       }
48422 
48423       // Read pixel data
48424       assign(dx,cimg::abs(dy),1,3,0);
48425       switch (bpp) {
48426       case 1 : { // Monochrome
48427         if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) {
48428           if (buf_size>=cimg_iobuffer) {
48429             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48430             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48431           }
48432           unsigned char mask = 0x80, val = 0;
48433           cimg_forX(*this,x) {
48434             if (mask==0x80) val = *(ptrs++);
48435             const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
48436             (*this)(x,y,2) = (T)*(col++);
48437             (*this)(x,y,1) = (T)*(col++);
48438             (*this)(x,y,0) = (T)*(col++);
48439             mask = cimg::ror(mask);
48440           }
48441           ptrs+=align_bytes;
48442         }
48443       } break;
48444       case 4 : { // 16 colors
48445         if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) {
48446           if (buf_size>=cimg_iobuffer) {
48447             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48448             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48449           }
48450           unsigned char mask = 0xF0, val = 0;
48451           cimg_forX(*this,x) {
48452             if (mask==0xF0) val = *(ptrs++);
48453             const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
48454             const unsigned char *col = (unsigned char*)(colormap._data + color);
48455             (*this)(x,y,2) = (T)*(col++);
48456             (*this)(x,y,1) = (T)*(col++);
48457             (*this)(x,y,0) = (T)*(col++);
48458             mask = cimg::ror(mask,4);
48459           }
48460           ptrs+=align_bytes;
48461         }
48462       } break;
48463       case 8 : { // 256 colors
48464         if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) {
48465           if (buf_size>=cimg_iobuffer) {
48466             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48467             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48468           }
48469           cimg_forX(*this,x) {
48470             const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
48471             (*this)(x,y,2) = (T)*(col++);
48472             (*this)(x,y,1) = (T)*(col++);
48473             (*this)(x,y,0) = (T)*(col++);
48474           }
48475           ptrs+=align_bytes;
48476         }
48477       } break;
48478       case 16 : { // 16 bits colors
48479         for (int y = height() - 1; y>=0; --y) {
48480           if (buf_size>=cimg_iobuffer) {
48481             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48482             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48483           }
48484           cimg_forX(*this,x) {
48485             const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
48486             const unsigned short col = (unsigned short)(c1|(c2<<8));
48487             (*this)(x,y,2) = (T)(col&0x1F);
48488             (*this)(x,y,1) = (T)((col>>5)&0x1F);
48489             (*this)(x,y,0) = (T)((col>>10)&0x1F);
48490           }
48491           ptrs+=align_bytes;
48492         }
48493       } break;
48494       case 24 : { // 24 bits colors
48495         for (int y = height() - 1; y>=0; --y) {
48496           if (buf_size>=cimg_iobuffer) {
48497             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48498             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48499           }
48500           cimg_forX(*this,x) {
48501             (*this)(x,y,2) = (T)*(ptrs++);
48502             (*this)(x,y,1) = (T)*(ptrs++);
48503             (*this)(x,y,0) = (T)*(ptrs++);
48504           }
48505           ptrs+=align_bytes;
48506         }
48507       } break;
48508       case 32 : { // 32 bits colors
48509         for (int y = height() - 1; y>=0; --y) {
48510           if (buf_size>=cimg_iobuffer) {
48511             if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
48512             cimg::fseek(nfile,align_bytes,SEEK_CUR);
48513           }
48514           cimg_forX(*this,x) {
48515             (*this)(x,y,2) = (T)*(ptrs++);
48516             (*this)(x,y,1) = (T)*(ptrs++);
48517             (*this)(x,y,0) = (T)*(ptrs++);
48518             ++ptrs;
48519           }
48520           ptrs+=align_bytes;
48521         }
48522       } break;
48523       }
48524       if (dy<0) mirror('y');
48525       if (!file) cimg::fclose(nfile);
48526       return *this;
48527     }
48528 
48529     //! Load image from a JPEG file.
48530     /**
48531        \param filename Filename, as a C-string.
48532     **/
48533     CImg<T>& load_jpeg(const char *const filename) {
48534       return _load_jpeg(0,filename);
48535     }
48536 
48537     //! Load image from a JPEG file \newinstance.
48538     static CImg<T> get_load_jpeg(const char *const filename) {
48539       return CImg<T>().load_jpeg(filename);
48540     }
48541 
48542     //! Load image from a JPEG file \overloading.
48543     CImg<T>& load_jpeg(std::FILE *const file) {
48544       return _load_jpeg(file,0);
48545     }
48546 
48547     //! Load image from a JPEG file \newinstance.
48548     static CImg<T> get_load_jpeg(std::FILE *const file) {
48549       return CImg<T>().load_jpeg(file);
48550     }
48551 
48552     // Custom error handler for libjpeg.
48553 #ifdef cimg_use_jpeg
48554     struct _cimg_error_mgr {
48555       struct jpeg_error_mgr original;
48556       jmp_buf setjmp_buffer;
48557       char message[JMSG_LENGTH_MAX];
48558     };
48559 
48560     typedef struct _cimg_error_mgr *_cimg_error_ptr;
48561 
48562     METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
48563       _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err;  // Return control to the setjmp point
48564       (*cinfo->err->format_message)(cinfo,c_err->message);
48565       jpeg_destroy(cinfo);  // Clean memory and temp files.
48566       longjmp(c_err->setjmp_buffer,1);
48567     }
48568 #endif
48569 
48570     CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
48571       if (!file && !filename)
48572         throw CImgArgumentException(_cimg_instance
48573                                     "load_jpeg(): Specified filename is (null).",
48574                                     cimg_instance);
48575 
48576 #ifndef cimg_use_jpeg
48577       if (file)
48578         throw CImgIOException(_cimg_instance
48579                               "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
48580                               cimg_instance);
48581       else return load_other(filename);
48582 #else
48583 
48584       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
48585       struct jpeg_decompress_struct cinfo;
48586       struct _cimg_error_mgr jerr;
48587       cinfo.err = jpeg_std_error(&jerr.original);
48588       jerr.original.error_exit = _cimg_jpeg_error_exit;
48589       if (setjmp(jerr.setjmp_buffer)) { // JPEG error
48590         if (!file) cimg::fclose(nfile);
48591         throw CImgIOException(_cimg_instance
48592                              "load_jpeg(): Error message returned by libjpeg: %s.",
48593                              cimg_instance,jerr.message);
48594       }
48595 
48596       jpeg_create_decompress(&cinfo);
48597       jpeg_stdio_src(&cinfo,nfile);
48598       jpeg_read_header(&cinfo,TRUE);
48599       jpeg_start_decompress(&cinfo);
48600 
48601       if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
48602         if (!file) {
48603           cimg::fclose(nfile);
48604           return load_other(filename);
48605         } else
48606           throw CImgIOException(_cimg_instance
48607                                 "load_jpeg(): Failed to load JPEG data from file '%s'.",
48608                                 cimg_instance,filename?filename:"(FILE*)");
48609       }
48610       CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
48611       JSAMPROW row_pointer[1];
48612       try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); }
48613       catch (...) { if (!file) cimg::fclose(nfile); throw; }
48614       T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height,
48615         *ptr_a = _data + 3UL*_width*_height;
48616       while (cinfo.output_scanline<cinfo.output_height) {
48617         *row_pointer = buffer._data;
48618         if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
48619           cimg::warn(_cimg_instance
48620                      "load_jpeg(): Incomplete data in file '%s'.",
48621                      cimg_instance,filename?filename:"(FILE*)");
48622           break;
48623         }
48624         const unsigned char *ptrs = buffer._data;
48625         switch (_spectrum) {
48626         case 1 : {
48627           cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
48628         } break;
48629         case 3 : {
48630           cimg_forX(*this,x) {
48631             *(ptr_r++) = (T)*(ptrs++);
48632             *(ptr_g++) = (T)*(ptrs++);
48633             *(ptr_b++) = (T)*(ptrs++);
48634           }
48635         } break;
48636         case 4 : {
48637           cimg_forX(*this,x) {
48638             *(ptr_r++) = (T)*(ptrs++);
48639             *(ptr_g++) = (T)*(ptrs++);
48640             *(ptr_b++) = (T)*(ptrs++);
48641             *(ptr_a++) = (T)*(ptrs++);
48642           }
48643         } break;
48644         }
48645       }
48646       jpeg_finish_decompress(&cinfo);
48647       jpeg_destroy_decompress(&cinfo);
48648       if (!file) cimg::fclose(nfile);
48649       return *this;
48650 #endif
48651     }
48652 
48653     //! Load image from a file, using Magick++ library.
48654     /**
48655        \param filename Filename, as a C-string.
48656     **/
48657     // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
48658     //   This is experimental code, not much tested, use with care.
48659     CImg<T>& load_magick(const char *const filename) {
48660       if (!filename)
48661         throw CImgArgumentException(_cimg_instance
48662                                     "load_magick(): Specified filename is (null).",
48663                                     cimg_instance);
48664 #ifdef cimg_use_magick
48665       Magick::Image image(filename);
48666       const unsigned int W = image.size().width(), H = image.size().height();
48667       switch (image.type()) {
48668       case Magick::PaletteMatteType :
48669       case Magick::TrueColorMatteType :
48670       case Magick::ColorSeparationType : {
48671         assign(W,H,1,4);
48672         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);
48673         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
48674         for (ulongT off = (ulongT)W*H; off; --off) {
48675           *(ptr_r++) = (T)(pixels->red);
48676           *(ptr_g++) = (T)(pixels->green);
48677           *(ptr_b++) = (T)(pixels->blue);
48678           *(ptr_a++) = (T)(pixels->opacity);
48679           ++pixels;
48680         }
48681       } break;
48682       case Magick::PaletteType :
48683       case Magick::TrueColorType : {
48684         assign(W,H,1,3);
48685         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
48686         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
48687         for (ulongT off = (ulongT)W*H; off; --off) {
48688           *(ptr_r++) = (T)(pixels->red);
48689           *(ptr_g++) = (T)(pixels->green);
48690           *(ptr_b++) = (T)(pixels->blue);
48691           ++pixels;
48692         }
48693       } break;
48694       case Magick::GrayscaleMatteType : {
48695         assign(W,H,1,2);
48696         T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
48697         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
48698         for (ulongT off = (ulongT)W*H; off; --off) {
48699           *(ptr_r++) = (T)(pixels->red);
48700           *(ptr_a++) = (T)(pixels->opacity);
48701           ++pixels;
48702         }
48703       } break;
48704       default : {
48705         assign(W,H,1,1);
48706         T *ptr_r = data(0,0,0,0);
48707         Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
48708         for (ulongT off = (ulongT)W*H; off; --off) {
48709           *(ptr_r++) = (T)(pixels->red);
48710           ++pixels;
48711         }
48712       }
48713       }
48714       return *this;
48715 #else
48716       throw CImgIOException(_cimg_instance
48717                             "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
48718                             cimg_instance,
48719                             filename);
48720 #endif
48721     }
48722 
48723     //! Load image from a file, using Magick++ library \newinstance.
48724     static CImg<T> get_load_magick(const char *const filename) {
48725       return CImg<T>().load_magick(filename);
48726     }
48727 
48728     //! Load image from a PNG file.
48729     /**
48730        \param filename Filename, as a C-string.
48731        \param[out] bits_per_pixel Number of bits per pixels used to store pixel values in the image file.
48732     **/
48733     CImg<T>& load_png(const char *const filename, unsigned int *const bits_per_pixel=0) {
48734       return _load_png(0,filename,bits_per_pixel);
48735     }
48736 
48737     //! Load image from a PNG file \newinstance.
48738     static CImg<T> get_load_png(const char *const filename, unsigned int *const bits_per_pixel=0) {
48739       return CImg<T>().load_png(filename,bits_per_pixel);
48740     }
48741 
48742     //! Load image from a PNG file \overloading.
48743     CImg<T>& load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) {
48744       return _load_png(file,0,bits_per_pixel);
48745     }
48746 
48747     //! Load image from a PNG file \newinstance.
48748     static CImg<T> get_load_png(std::FILE *const file, unsigned int *const bits_per_pixel=0) {
48749       return CImg<T>().load_png(file,bits_per_pixel);
48750     }
48751 
48752     // (Note: Most of this function has been written by Eric Fausett)
48753     CImg<T>& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_pixel) {
48754       if (!file && !filename)
48755         throw CImgArgumentException(_cimg_instance
48756                                     "load_png(): Specified filename is (null).",
48757                                     cimg_instance);
48758 
48759 #ifndef cimg_use_png
48760       cimg::unused(bits_per_pixel);
48761       if (file)
48762         throw CImgIOException(_cimg_instance
48763                               "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
48764                               cimg_instance);
48765 
48766       else return load_other(filename);
48767 #else
48768       // Open file and check for PNG validity
48769 #if defined __GNUC__
48770       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning.
48771       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
48772 #else
48773       const char *nfilename = filename;
48774       std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb");
48775 #endif
48776       unsigned char pngCheck[8] = { 0 };
48777       cimg::fread(pngCheck,8,(std::FILE*)nfile);
48778       if (png_sig_cmp(pngCheck,0,8)) {
48779         if (!file) cimg::fclose(nfile);
48780         throw CImgIOException(_cimg_instance
48781                               "load_png(): Invalid PNG file '%s'.",
48782                               cimg_instance,
48783                               nfilename?nfilename:"(FILE*)");
48784       }
48785 
48786       // Setup PNG structures for read
48787       png_voidp user_error_ptr = 0;
48788       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
48789       png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
48790       if (!png_ptr) {
48791         if (!file) cimg::fclose(nfile);
48792         throw CImgIOException(_cimg_instance
48793                               "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
48794                               cimg_instance,
48795                               nfilename?nfilename:"(FILE*)");
48796       }
48797       png_infop info_ptr = png_create_info_struct(png_ptr);
48798       if (!info_ptr) {
48799         if (!file) cimg::fclose(nfile);
48800         png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
48801         throw CImgIOException(_cimg_instance
48802                               "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
48803                               cimg_instance,
48804                               nfilename?nfilename:"(FILE*)");
48805       }
48806       png_infop end_info = png_create_info_struct(png_ptr);
48807       if (!end_info) {
48808         if (!file) cimg::fclose(nfile);
48809         png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
48810         throw CImgIOException(_cimg_instance
48811                               "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
48812                               cimg_instance,
48813                               nfilename?nfilename:"(FILE*)");
48814       }
48815 
48816       // Error handling callback for png file reading
48817       if (setjmp(png_jmpbuf(png_ptr))) {
48818         if (!file) cimg::fclose((std::FILE*)nfile);
48819         png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
48820         throw CImgIOException(_cimg_instance
48821                               "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
48822                               cimg_instance,
48823                               nfilename?nfilename:"(FILE*)");
48824       }
48825       png_init_io(png_ptr, nfile);
48826       png_set_sig_bytes(png_ptr, 8);
48827 
48828       // Get PNG Header Info up to data block
48829       png_read_info(png_ptr,info_ptr);
48830       png_uint_32 W, H;
48831       int bit_depth, color_type, interlace_type;
48832       bool is_gray = false;
48833       png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
48834       if (bits_per_pixel) *bits_per_pixel = (unsigned int)bit_depth;
48835 
48836       // Transforms to unify image data
48837       if (color_type==PNG_COLOR_TYPE_PALETTE) {
48838         png_set_palette_to_rgb(png_ptr);
48839         color_type = PNG_COLOR_TYPE_RGB;
48840         bit_depth = 8;
48841       }
48842       if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
48843         png_set_expand_gray_1_2_4_to_8(png_ptr);
48844         is_gray = true;
48845         bit_depth = 8;
48846       }
48847       if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
48848         png_set_tRNS_to_alpha(png_ptr);
48849         color_type |= PNG_COLOR_MASK_ALPHA;
48850       }
48851       if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
48852         png_set_gray_to_rgb(png_ptr);
48853         color_type |= PNG_COLOR_MASK_COLOR;
48854         is_gray = true;
48855       }
48856       if (color_type==PNG_COLOR_TYPE_RGB)
48857         png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
48858 
48859       png_read_update_info(png_ptr,info_ptr);
48860       if (bit_depth!=8 && bit_depth!=16) {
48861         if (!file) cimg::fclose(nfile);
48862         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
48863         throw CImgIOException(_cimg_instance
48864                               "load_png(): Invalid bit depth %u in file '%s'.",
48865                               cimg_instance,
48866                               bit_depth,nfilename?nfilename:"(FILE*)");
48867       }
48868       const int byte_depth = bit_depth>>3;
48869 
48870       // Allocate Memory for Image Read
48871       png_bytep *const imgData = new png_bytep[H];
48872       for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[(size_t)byte_depth*4*W];
48873       png_read_image(png_ptr,imgData);
48874       png_read_end(png_ptr,end_info);
48875 
48876       // Read pixel data
48877       if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
48878         if (!file) cimg::fclose(nfile);
48879         png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
48880         throw CImgIOException(_cimg_instance
48881                               "load_png(): Invalid color coding type %u in file '%s'.",
48882                               cimg_instance,
48883                               color_type,nfilename?nfilename:"(FILE*)");
48884       }
48885       const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
48886       try { assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0)); }
48887       catch (...) { if (!file) cimg::fclose(nfile); throw; }
48888       T
48889         *ptr_r = data(0,0,0,0),
48890         *ptr_g = is_gray?0:data(0,0,0,1),
48891         *ptr_b = is_gray?0:data(0,0,0,2),
48892         *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
48893       switch (bit_depth) {
48894       case 8 : {
48895         cimg_forY(*this,y) {
48896           const unsigned char *ptrs = (unsigned char*)imgData[y];
48897           cimg_forX(*this,x) {
48898             *(ptr_r++) = (T)*(ptrs++);
48899             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
48900             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
48901             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
48902           }
48903         }
48904       } break;
48905       case 16 : {
48906         cimg_forY(*this,y) {
48907           const unsigned short *ptrs = (unsigned short*)(imgData[y]);
48908           if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
48909           cimg_forX(*this,x) {
48910             *(ptr_r++) = (T)*(ptrs++);
48911             if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
48912             if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
48913             if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
48914           }
48915         }
48916       } break;
48917       }
48918       png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
48919 
48920       // Deallocate Image Read Memory
48921       cimg_forY(*this,n) delete[] imgData[n];
48922       delete[] imgData;
48923       if (!file) cimg::fclose(nfile);
48924       return *this;
48925 #endif
48926     }
48927 
48928     //! Load image from a PNM file.
48929     /**
48930       \param filename Filename, as a C-string.
48931     **/
48932     CImg<T>& load_pnm(const char *const filename) {
48933       return _load_pnm(0,filename);
48934     }
48935 
48936     //! Load image from a PNM file \newinstance.
48937     static CImg<T> get_load_pnm(const char *const filename) {
48938       return CImg<T>().load_pnm(filename);
48939     }
48940 
48941     //! Load image from a PNM file \overloading.
48942     CImg<T>& load_pnm(std::FILE *const file) {
48943       return _load_pnm(file,0);
48944     }
48945 
48946     //! Load image from a PNM file \newinstance.
48947     static CImg<T> get_load_pnm(std::FILE *const file) {
48948       return CImg<T>().load_pnm(file);
48949     }
48950 
48951     CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
48952       if (!file && !filename)
48953         throw CImgArgumentException(_cimg_instance
48954                                     "load_pnm(): Specified filename is (null).",
48955                                     cimg_instance);
48956 
48957       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
48958       unsigned int ppm_type, W, H, D = 1, colormax = 255;
48959       CImg<charT> item(16384,1,1,1,0);
48960       int err, rval, gval, bval;
48961       const longT cimg_iobuffer = (longT)24*1024*1024;
48962       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
48963       if (cimg_sscanf(item," P%u",&ppm_type)!=1) {
48964         if (!file) cimg::fclose(nfile);
48965         throw CImgIOException(_cimg_instance
48966                               "load_pnm(): PNM header not found in file '%s'.",
48967                               cimg_instance,
48968                               filename?filename:"(FILE*)");
48969       }
48970       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
48971       if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
48972         if (!file) cimg::fclose(nfile);
48973         throw CImgIOException(_cimg_instance
48974                               "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
48975                               cimg_instance,
48976                               filename?filename:"(FILE*)");
48977       }
48978       if (ppm_type!=1 && ppm_type!=4) {
48979         if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
48980           while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
48981           if (cimg_sscanf(item,"%u",&colormax)!=1)
48982             cimg::warn(_cimg_instance
48983                        "load_pnm(): COLORMAX field is undefined in file '%s'.",
48984                        cimg_instance,
48985                        filename?filename:"(FILE*)");
48986         } else { colormax = D; D = 1; }
48987       }
48988       std::fgetc(nfile);
48989 
48990       switch (ppm_type) {
48991       case 1 : { // 2d b&w ascii.
48992         assign(W,H,1,1);
48993         T* ptrd = _data;
48994         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
48995       } break;
48996       case 2 : { // 2d grey ascii.
48997         assign(W,H,1,1);
48998         T* ptrd = _data;
48999         cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
49000       } break;
49001       case 3 : { // 2d color ascii.
49002         assign(W,H,1,3);
49003         T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
49004         cimg_forXY(*this,x,y) {
49005           if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) {
49006             *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval;
49007           } else break;
49008         }
49009       } break;
49010       case 4 : { // 2d b&w binary (support 3D PINK extension).
49011         CImg<ucharT> raw;
49012         assign(W,H,D,1);
49013         T *ptrd = data(0,0,0,0);
49014         unsigned int w = 0, h = 0, d = 0;
49015         for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
49016           raw.assign(std::min(to_read,cimg_iobuffer));
49017           cimg::fread(raw._data,raw._width,nfile);
49018           to_read-=raw._width;
49019           const unsigned char *ptrs = raw._data;
49020           unsigned char mask = 0, val = 0;
49021           for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) {
49022             if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
49023             *(ptrd++) = (T)((val&mask)?0:255);
49024             if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
49025           }
49026         }
49027       } break;
49028       case 5 : case 7 : { // 2d/3d grey binary (support 3D PINK extension).
49029         if (colormax<256) { // 8 bits.
49030           CImg<ucharT> raw;
49031           assign(W,H,D,1);
49032           T *ptrd = data(0,0,0,0);
49033           for (longT to_read = (longT)size(); to_read>0; ) {
49034             raw.assign(std::min(to_read,cimg_iobuffer));
49035             cimg::fread(raw._data,raw._width,nfile);
49036             to_read-=raw._width;
49037             const unsigned char *ptrs = raw._data;
49038             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
49039           }
49040         } else { // 16 bits.
49041           CImg<ushortT> raw;
49042           assign(W,H,D,1);
49043           T *ptrd = data(0,0,0,0);
49044           for (longT to_read = (longT)size(); to_read>0; ) {
49045             raw.assign(std::min(to_read,cimg_iobuffer/2));
49046             cimg::fread(raw._data,raw._width,nfile);
49047 	    if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
49048             to_read-=raw._width;
49049             const unsigned short *ptrs = raw._data;
49050             for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
49051           }
49052         }
49053       } break;
49054       case 6 : { // 2d color binary.
49055         if (colormax<256) { // 8 bits.
49056           CImg<ucharT> raw;
49057           assign(W,H,1,3);
49058           T
49059             *ptr_r = data(0,0,0,0),
49060             *ptr_g = data(0,0,0,1),
49061             *ptr_b = data(0,0,0,2);
49062           for (longT to_read = (longT)size(); to_read>0; ) {
49063             raw.assign(std::min(to_read,cimg_iobuffer));
49064             cimg::fread(raw._data,raw._width,nfile);
49065             to_read-=raw._width;
49066             const unsigned char *ptrs = raw._data;
49067             for (ulongT off = (ulongT)raw._width/3; off; --off) {
49068               *(ptr_r++) = (T)*(ptrs++);
49069               *(ptr_g++) = (T)*(ptrs++);
49070               *(ptr_b++) = (T)*(ptrs++);
49071             }
49072           }
49073         } else { // 16 bits.
49074           CImg<ushortT> raw;
49075           assign(W,H,1,3);
49076           T
49077             *ptr_r = data(0,0,0,0),
49078             *ptr_g = data(0,0,0,1),
49079             *ptr_b = data(0,0,0,2);
49080           for (longT to_read = (longT)size(); to_read>0; ) {
49081             raw.assign(std::min(to_read,cimg_iobuffer/2));
49082             cimg::fread(raw._data,raw._width,nfile);
49083             if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
49084             to_read-=raw._width;
49085             const unsigned short *ptrs = raw._data;
49086             for (ulongT off = (ulongT)raw._width/3; off; --off) {
49087               *(ptr_r++) = (T)*(ptrs++);
49088               *(ptr_g++) = (T)*(ptrs++);
49089               *(ptr_b++) = (T)*(ptrs++);
49090             }
49091           }
49092         }
49093       } break;
49094       case 8 : { // 2d/3d grey binary with int32 integers (PINK extension).
49095         CImg<intT> raw;
49096         assign(W,H,D,1);
49097         T *ptrd = data(0,0,0,0);
49098         for (longT to_read = (longT)size(); to_read>0; ) {
49099           raw.assign(std::min(to_read,cimg_iobuffer));
49100           cimg::fread(raw._data,raw._width,nfile);
49101           to_read-=raw._width;
49102           const int *ptrs = raw._data;
49103           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
49104         }
49105       } break;
49106       case 9 : { // 2d/3d grey binary with float values (PINK extension).
49107         CImg<floatT> raw;
49108         assign(W,H,D,1);
49109         T *ptrd = data(0,0,0,0);
49110         for (longT to_read = (longT)size(); to_read>0; ) {
49111           raw.assign(std::min(to_read,cimg_iobuffer));
49112           cimg::fread(raw._data,raw._width,nfile);
49113           to_read-=raw._width;
49114           const float *ptrs = raw._data;
49115           for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
49116         }
49117       } break;
49118       default :
49119         assign();
49120         if (!file) cimg::fclose(nfile);
49121         throw CImgIOException(_cimg_instance
49122                               "load_pnm(): PNM type 'P%d' found, but type is not supported.",
49123                               cimg_instance,
49124                               filename?filename:"(FILE*)",ppm_type);
49125       }
49126       if (!file) cimg::fclose(nfile);
49127       return *this;
49128     }
49129 
49130     //! Load image from a PFM file.
49131     /**
49132       \param filename Filename, as a C-string.
49133     **/
49134     CImg<T>& load_pfm(const char *const filename) {
49135       return _load_pfm(0,filename);
49136     }
49137 
49138     //! Load image from a PFM file \newinstance.
49139     static CImg<T> get_load_pfm(const char *const filename) {
49140       return CImg<T>().load_pfm(filename);
49141     }
49142 
49143     //! Load image from a PFM file \overloading.
49144     CImg<T>& load_pfm(std::FILE *const file) {
49145       return _load_pfm(file,0);
49146     }
49147 
49148     //! Load image from a PFM file \newinstance.
49149     static CImg<T> get_load_pfm(std::FILE *const file) {
49150       return CImg<T>().load_pfm(file);
49151     }
49152 
49153     CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
49154       if (!file && !filename)
49155         throw CImgArgumentException(_cimg_instance
49156                                     "load_pfm(): Specified filename is (null).",
49157                                     cimg_instance);
49158 
49159       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
49160       char pfm_type;
49161       CImg<charT> item(16384,1,1,1,0);
49162       int W = 0, H = 0, err = 0;
49163       double scale = 0;
49164       while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
49165       if (cimg_sscanf(item," P%c",&pfm_type)!=1) {
49166         if (!file) cimg::fclose(nfile);
49167         throw CImgIOException(_cimg_instance
49168                               "load_pfm(): PFM header not found in file '%s'.",
49169                               cimg_instance,
49170                               filename?filename:"(FILE*)");
49171       }
49172       while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
49173       if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) {
49174         if (!file) cimg::fclose(nfile);
49175         throw CImgIOException(_cimg_instance
49176                               "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
49177                               cimg_instance,
49178                               filename?filename:"(FILE*)");
49179       } else if (W<=0 || H<=0) {
49180         if (!file) cimg::fclose(nfile);
49181         throw CImgIOException(_cimg_instance
49182                               "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.",
49183                               cimg_instance,W,H,
49184                               filename?filename:"(FILE*)");
49185       }
49186       if (err==2) {
49187         while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
49188         if (cimg_sscanf(item,"%lf",&scale)!=1)
49189           cimg::warn(_cimg_instance
49190                      "load_pfm(): SCALE field is undefined in file '%s'.",
49191                      cimg_instance,
49192                      filename?filename:"(FILE*)");
49193       }
49194       std::fgetc(nfile);
49195       const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
49196       if (is_color) {
49197         assign(W,H,1,3,(T)0);
49198         CImg<floatT> buf(3*W);
49199         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
49200         cimg_forY(*this,y) {
49201           cimg::fread(buf._data,3*W,nfile);
49202           if (is_inverted) cimg::invert_endianness(buf._data,3*W);
49203           const float *ptrs = buf._data;
49204           cimg_forX(*this,x) {
49205             *(ptr_r++) = (T)*(ptrs++);
49206             *(ptr_g++) = (T)*(ptrs++);
49207             *(ptr_b++) = (T)*(ptrs++);
49208           }
49209         }
49210       } else {
49211         assign(W,H,1,1,(T)0);
49212         CImg<floatT> buf(W);
49213         T *ptrd = data(0,0,0,0);
49214         cimg_forY(*this,y) {
49215           cimg::fread(buf._data,W,nfile);
49216           if (is_inverted) cimg::invert_endianness(buf._data,W);
49217           const float *ptrs = buf._data;
49218           cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
49219         }
49220       }
49221       if (!file) cimg::fclose(nfile);
49222       return mirror('y');  // Most of the .pfm files are flipped along the y-axis.
49223     }
49224 
49225     //! Load image from a RGB file.
49226     /**
49227       \param filename Filename, as a C-string.
49228       \param dimw Width of the image buffer.
49229       \param dimh Height of the image buffer.
49230     **/
49231     CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
49232       return _load_rgb(0,filename,dimw,dimh);
49233     }
49234 
49235     //! Load image from a RGB file \newinstance.
49236     static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
49237       return CImg<T>().load_rgb(filename,dimw,dimh);
49238     }
49239 
49240     //! Load image from a RGB file \overloading.
49241     CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
49242       return _load_rgb(file,0,dimw,dimh);
49243     }
49244 
49245     //! Load image from a RGB file \newinstance.
49246     static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
49247       return CImg<T>().load_rgb(file,dimw,dimh);
49248     }
49249 
49250     CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
49251                        const unsigned int dimw, const unsigned int dimh) {
49252       if (!file && !filename)
49253         throw CImgArgumentException(_cimg_instance
49254                                     "load_rgb(): Specified filename is (null).",
49255                                     cimg_instance);
49256 
49257       if (!dimw || !dimh) return assign();
49258       const longT cimg_iobuffer = (longT)24*1024*1024;
49259       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
49260       CImg<ucharT> raw;
49261       assign(dimw,dimh,1,3);
49262       T
49263         *ptr_r = data(0,0,0,0),
49264         *ptr_g = data(0,0,0,1),
49265         *ptr_b = data(0,0,0,2);
49266       for (longT to_read = (longT)size(); to_read>0; ) {
49267         raw.assign(std::min(to_read,cimg_iobuffer));
49268         cimg::fread(raw._data,raw._width,nfile);
49269         to_read-=raw._width;
49270         const unsigned char *ptrs = raw._data;
49271         for (ulongT off = raw._width/3UL; off; --off) {
49272           *(ptr_r++) = (T)*(ptrs++);
49273           *(ptr_g++) = (T)*(ptrs++);
49274           *(ptr_b++) = (T)*(ptrs++);
49275         }
49276       }
49277       if (!file) cimg::fclose(nfile);
49278       return *this;
49279     }
49280 
49281     //! Load image from a RGBA file.
49282     /**
49283        \param filename Filename, as a C-string.
49284        \param dimw Width of the image buffer.
49285        \param dimh Height of the image buffer.
49286     **/
49287     CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
49288       return _load_rgba(0,filename,dimw,dimh);
49289     }
49290 
49291     //! Load image from a RGBA file \newinstance.
49292     static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
49293       return CImg<T>().load_rgba(filename,dimw,dimh);
49294     }
49295 
49296     //! Load image from a RGBA file \overloading.
49297     CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
49298       return _load_rgba(file,0,dimw,dimh);
49299     }
49300 
49301     //! Load image from a RGBA file \newinstance.
49302     static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
49303       return CImg<T>().load_rgba(file,dimw,dimh);
49304     }
49305 
49306     CImg<T>& _load_rgba(std::FILE *const file, const char *const filename,
49307                         const unsigned int dimw, const unsigned int dimh) {
49308       if (!file && !filename)
49309         throw CImgArgumentException(_cimg_instance
49310                                     "load_rgba(): Specified filename is (null).",
49311                                     cimg_instance);
49312 
49313       if (!dimw || !dimh) return assign();
49314       const longT cimg_iobuffer = (longT)24*1024*1024;
49315       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
49316       CImg<ucharT> raw;
49317       assign(dimw,dimh,1,4);
49318       T
49319         *ptr_r = data(0,0,0,0),
49320         *ptr_g = data(0,0,0,1),
49321         *ptr_b = data(0,0,0,2),
49322         *ptr_a = data(0,0,0,3);
49323       for (longT to_read = (longT)size(); to_read>0; ) {
49324         raw.assign(std::min(to_read,cimg_iobuffer));
49325         cimg::fread(raw._data,raw._width,nfile);
49326         to_read-=raw._width;
49327         const unsigned char *ptrs = raw._data;
49328         for (ulongT off = raw._width/4UL; off; --off) {
49329           *(ptr_r++) = (T)*(ptrs++);
49330           *(ptr_g++) = (T)*(ptrs++);
49331           *(ptr_b++) = (T)*(ptrs++);
49332           *(ptr_a++) = (T)*(ptrs++);
49333         }
49334       }
49335       if (!file) cimg::fclose(nfile);
49336       return *this;
49337     }
49338 
49339     //! Load image from a TIFF file.
49340     /**
49341        \param filename Filename, as a C-string.
49342        \param first_frame First frame to read (for multi-pages tiff).
49343        \param last_frame Last frame to read (for multi-pages tiff).
49344        \param step_frame Step value of frame reading.
49345        \param[out] voxel_size Voxel size, as stored in the filename.
49346        \param[out] description Description, as stored in the filename.
49347        \note
49348        - libtiff support is enabled by defining the precompilation
49349         directive \c cimg_use_tif.
49350        - When libtiff is enabled, 2D and 3D (multipage) several
49351         channel per pixel are supported for
49352         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
49353        - If \c cimg_use_tif is not defined at compile time the
49354         function uses CImg<T>& load_other(const char*).
49355      **/
49356     CImg<T>& load_tiff(const char *const filename,
49357 		       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
49358 		       const unsigned int step_frame=1,
49359                        float *const voxel_size=0,
49360                        CImg<charT> *const description=0) {
49361       if (!filename)
49362         throw CImgArgumentException(_cimg_instance
49363                                     "load_tiff(): Specified filename is (null).",
49364                                     cimg_instance);
49365 
49366       const unsigned int
49367 	nfirst_frame = first_frame<last_frame?first_frame:last_frame,
49368 	nstep_frame = step_frame?step_frame:1;
49369       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
49370 
49371 #ifndef cimg_use_tiff
49372       cimg::unused(voxel_size,description);
49373       if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
49374         throw CImgArgumentException(_cimg_instance
49375                                     "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
49376                                     cimg_instance,
49377                                     filename);
49378       return load_other(filename);
49379 #else
49380 #if cimg_verbosity<3
49381       TIFFSetWarningHandler(0);
49382       TIFFSetErrorHandler(0);
49383 #endif
49384       TIFF *tif = TIFFOpen(filename,"r");
49385       if (tif) {
49386         unsigned int nb_images = 0;
49387         do ++nb_images; while (TIFFReadDirectory(tif));
49388         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
49389           cimg::warn(_cimg_instance
49390                      "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
49391                      cimg_instance,
49392                      filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
49393 
49394         if (nfirst_frame>=nb_images) return assign();
49395         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
49396         TIFFSetDirectory(tif,0);
49397         CImg<T> frame;
49398         for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
49399           frame._load_tiff(tif,l,voxel_size,description);
49400           if (l==nfirst_frame)
49401             assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum);
49402           if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
49403             resize(std::max(frame._width,_width),
49404                    std::max(frame._height,_height),-100,
49405                    std::max(frame._spectrum,_spectrum),0);
49406           draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame);
49407         }
49408         TIFFClose(tif);
49409       } else throw CImgIOException(_cimg_instance
49410                                    "load_tiff(): Failed to open file '%s'.",
49411                                    cimg_instance,
49412                                    filename);
49413       return *this;
49414 #endif
49415     }
49416 
49417     //! Load image from a TIFF file \newinstance.
49418     static CImg<T> get_load_tiff(const char *const filename,
49419 				 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
49420 				 const unsigned int step_frame=1,
49421                                  float *const voxel_size=0,
49422                                  CImg<charT> *const description=0) {
49423       return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description);
49424     }
49425 
49426     // (Original contribution by Jerome Boulanger).
49427 #ifdef cimg_use_tiff
49428     template<typename t>
49429     void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel,
49430                                  const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
49431       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
49432       if (buf) {
49433         for (unsigned int row = 0; row<ny; row+=th)
49434           for (unsigned int col = 0; col<nx; col+=tw) {
49435             if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
49436               _TIFFfree(buf); TIFFClose(tif);
49437               throw CImgIOException(_cimg_instance
49438                                     "load_tiff(): Invalid tile in file '%s'.",
49439                                     cimg_instance,
49440                                     TIFFFileName(tif));
49441             }
49442             const t *ptr = buf;
49443             for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
49444               for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
49445                 for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
49446                   (*this)(cc,rr,vv) = (T)(ptr[(rr - row)*th*samplesperpixel + (cc - col)*samplesperpixel + vv]);
49447           }
49448         _TIFFfree(buf);
49449       }
49450     }
49451 
49452     template<typename t>
49453     void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel,
49454                                    const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
49455       t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
49456       if (buf) {
49457         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
49458           for (unsigned int row = 0; row<ny; row+=th)
49459             for (unsigned int col = 0; col<nx; col+=tw) {
49460               if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
49461                 _TIFFfree(buf); TIFFClose(tif);
49462                 throw CImgIOException(_cimg_instance
49463                                       "load_tiff(): Invalid tile in file '%s'.",
49464                                       cimg_instance,
49465                                       TIFFFileName(tif));
49466               }
49467               const t *ptr = buf;
49468               for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
49469                 for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
49470                   (*this)(cc,rr,vv) = (T)*(ptr++);
49471             }
49472         _TIFFfree(buf);
49473       }
49474     }
49475 
49476     template<typename t>
49477     void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
49478       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
49479       if (buf) {
49480         uint32 row, rowsperstrip = (uint32)-1;
49481         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
49482         for (row = 0; row<ny; row+= rowsperstrip) {
49483           uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
49484           tstrip_t strip = TIFFComputeStrip(tif, row, 0);
49485           if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
49486             _TIFFfree(buf); TIFFClose(tif);
49487             throw CImgIOException(_cimg_instance
49488                                   "load_tiff(): Invalid strip in file '%s'.",
49489                                   cimg_instance,
49490                                   TIFFFileName(tif));
49491           }
49492           const t *ptr = buf;
49493           for (unsigned int rr = 0; rr<nrow; ++rr)
49494             for (unsigned int cc = 0; cc<nx; ++cc)
49495               for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row + rr,vv) = (T)*(ptr++);
49496         }
49497         _TIFFfree(buf);
49498       }
49499     }
49500 
49501     template<typename t>
49502     void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
49503       t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
49504       if (buf) {
49505         uint32 row, rowsperstrip = (uint32)-1;
49506         TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
49507         for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
49508           for (row = 0; row<ny; row+= rowsperstrip) {
49509             uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
49510             tstrip_t strip = TIFFComputeStrip(tif, row, vv);
49511             if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
49512               _TIFFfree(buf); TIFFClose(tif);
49513               throw CImgIOException(_cimg_instance
49514                                     "load_tiff(): Invalid strip in file '%s'.",
49515                                     cimg_instance,
49516                                     TIFFFileName(tif));
49517             }
49518             const t *ptr = buf;
49519             for (unsigned int rr = 0;rr<nrow; ++rr)
49520               for (unsigned int cc = 0; cc<nx; ++cc)
49521                 (*this)(cc,row + rr,vv) = (T)*(ptr++);
49522           }
49523         _TIFFfree(buf);
49524       }
49525     }
49526 
49527     CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory,
49528                         float *const voxel_size, CImg<charT> *const description) {
49529       if (!TIFFSetDirectory(tif,directory)) return assign();
49530       uint16 samplesperpixel = 1, bitspersample = 8, photo = 0;
49531       uint16 sampleformat = 1;
49532       uint32 nx = 1, ny = 1;
49533       const char *const filename = TIFFFileName(tif);
49534       const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
49535       TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
49536       TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
49537       TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
49538       TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
49539       TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
49540       if (voxel_size) {
49541         const char *s_description = 0;
49542         float vx = 0, vy = 0, vz = 0;
49543         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) {
49544           const char *s_desc = std::strstr(s_description,"VX=");
49545           if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format.
49546             voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz;
49547           }
49548           s_desc = std::strstr(s_description,"spacing=");
49549           if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // fiji format.
49550             voxel_size[2] = vz;
49551           }
49552         }
49553         TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size);
49554         TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1);
49555         voxel_size[0] = 1.0f/voxel_size[0];
49556         voxel_size[1] = 1.0f/voxel_size[1];
49557       }
49558       if (description) {
49559         const char *s_description = 0;
49560         if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description)
49561           CImg<charT>::string(s_description).move_to(*description);
49562       }
49563       const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel;
49564       assign(nx,ny,1,spectrum);
49565 
49566       if ((photo>=3 && sampleformat==1 &&
49567            (bitspersample==4 || bitspersample==8) &&
49568            (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) ||
49569           (bitspersample==1 && samplesperpixel==1)) {
49570         // Special case for unsigned color images.
49571         uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32));
49572         if (!raster) {
49573           _TIFFfree(raster); TIFFClose(tif);
49574           throw CImgException(_cimg_instance
49575                               "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
49576                               cimg_instance,
49577                               cimg::strbuffersize(nx*ny*sizeof(uint32)),filename);
49578         }
49579         TIFFReadRGBAImage(tif,nx,ny,raster,0);
49580         switch (spectrum) {
49581         case 1 :
49582           cimg_forXY(*this,x,y)
49583             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
49584           break;
49585         case 3 :
49586           cimg_forXY(*this,x,y) {
49587             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
49588             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 -y) + x]);
49589             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 -y) + x]);
49590           }
49591           break;
49592         case 4 :
49593           cimg_forXY(*this,x,y) {
49594             (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
49595             (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
49596             (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
49597             (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]);
49598           }
49599           break;
49600         }
49601         _TIFFfree(raster);
49602       } else { // Other cases.
49603         uint16 config;
49604         TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
49605         if (TIFFIsTiled(tif)) {
49606           uint32 tw = 1, th = 1;
49607           TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
49608           TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
49609           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
49610             case 8 :
49611               if (sampleformat==SAMPLEFORMAT_UINT)
49612                 _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
49613               else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
49614               break;
49615             case 16 :
49616               if (sampleformat==SAMPLEFORMAT_UINT)
49617                 _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
49618               else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
49619               break;
49620             case 32 :
49621               if (sampleformat==SAMPLEFORMAT_UINT)
49622                 _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
49623               else if (sampleformat==SAMPLEFORMAT_INT)
49624                 _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
49625               else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
49626               break;
49627             case 64 :
49628               if (sampleformat==SAMPLEFORMAT_UINT)
49629                 _load_tiff_tiled_contig<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
49630               else if (sampleformat==SAMPLEFORMAT_INT)
49631                 _load_tiff_tiled_contig<int64T>(tif,samplesperpixel,nx,ny,tw,th);
49632               else _load_tiff_tiled_contig<double>(tif,samplesperpixel,nx,ny,tw,th);
49633               break;
49634             } else switch (bitspersample) {
49635             case 8 :
49636               if (sampleformat==SAMPLEFORMAT_UINT)
49637                 _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
49638               else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
49639               break;
49640             case 16 :
49641               if (sampleformat==SAMPLEFORMAT_UINT)
49642                 _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
49643               else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
49644               break;
49645             case 32 :
49646               if (sampleformat==SAMPLEFORMAT_UINT)
49647                 _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
49648               else if (sampleformat==SAMPLEFORMAT_INT)
49649                 _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
49650               else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
49651               break;
49652             case 64 :
49653               if (sampleformat==SAMPLEFORMAT_UINT)
49654                 _load_tiff_tiled_separate<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
49655               else if (sampleformat==SAMPLEFORMAT_INT)
49656                 _load_tiff_tiled_separate<int64T>(tif,samplesperpixel,nx,ny,tw,th);
49657               else _load_tiff_tiled_separate<double>(tif,samplesperpixel,nx,ny,tw,th);
49658               break;
49659             }
49660         } else {
49661           if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
49662             case 8 :
49663               if (sampleformat==SAMPLEFORMAT_UINT)
49664                 _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
49665               else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
49666               break;
49667             case 16 :
49668               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
49669               else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
49670               break;
49671             case 32 :
49672               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
49673               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
49674               else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
49675               break;
49676             case 64 :
49677               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<uint64T>(tif,samplesperpixel,nx,ny);
49678               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int64T>(tif,samplesperpixel,nx,ny);
49679               else _load_tiff_contig<double>(tif,samplesperpixel,nx,ny);
49680               break;
49681             } else switch (bitspersample) {
49682             case 8 :
49683               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
49684               else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
49685               break;
49686             case 16 :
49687               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
49688               else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
49689               break;
49690             case 32 :
49691               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
49692               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
49693               else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
49694               break;
49695             case 64 :
49696               if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<uint64T>(tif,samplesperpixel,nx,ny);
49697               else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int64T>(tif,samplesperpixel,nx,ny);
49698               else _load_tiff_separate<double>(tif,samplesperpixel,nx,ny);
49699               break;
49700             }
49701         }
49702       }
49703       return *this;
49704     }
49705 #endif
49706 
49707     //! Load image from a MINC2 file.
49708     /**
49709         \param filename Filename, as a C-string.
49710     **/
49711     // (Original code by Haz-Edine Assemlal).
49712     CImg<T>& load_minc2(const char *const filename) {
49713       if (!filename)
49714         throw CImgArgumentException(_cimg_instance
49715                                     "load_minc2(): Specified filename is (null).",
49716                                     cimg_instance);
49717 #ifndef cimg_use_minc2
49718       return load_other(filename);
49719 #else
49720       minc::minc_1_reader rdr;
49721       rdr.open(filename);
49722       assign(rdr.ndim(1)?rdr.ndim(1):1,
49723              rdr.ndim(2)?rdr.ndim(2):1,
49724              rdr.ndim(3)?rdr.ndim(3):1,
49725              rdr.ndim(4)?rdr.ndim(4):1);
49726       if (cimg::type<T>::string()==cimg::type<unsigned char>::string())
49727         rdr.setup_read_byte();
49728       else if (cimg::type<T>::string()==cimg::type<int>::string())
49729         rdr.setup_read_int();
49730       else if (cimg::type<T>::string()==cimg::type<double>::string())
49731         rdr.setup_read_double();
49732       else
49733         rdr.setup_read_float();
49734       minc::load_standard_volume(rdr,this->_data);
49735       return *this;
49736 #endif
49737     }
49738 
49739     //! Load image from a MINC2 file \newinstance.
49740     static CImg<T> get_load_minc2(const char *const filename) {
49741       return CImg<T>().load_analyze(filename);
49742     }
49743 
49744     //! Load image from an ANALYZE7.5/NIFTI file.
49745     /**
49746        \param filename Filename, as a C-string.
49747        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
49748     **/
49749     CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
49750       return _load_analyze(0,filename,voxel_size);
49751     }
49752 
49753     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
49754     static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
49755       return CImg<T>().load_analyze(filename,voxel_size);
49756     }
49757 
49758     //! Load image from an ANALYZE7.5/NIFTI file \overloading.
49759     CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
49760       return _load_analyze(file,0,voxel_size);
49761     }
49762 
49763     //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
49764     static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
49765       return CImg<T>().load_analyze(file,voxel_size);
49766     }
49767 
49768     CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
49769       if (!file && !filename)
49770         throw CImgArgumentException(_cimg_instance
49771                                     "load_analyze(): Specified filename is (null).",
49772                                     cimg_instance);
49773 
49774       std::FILE *nfile_header = 0, *nfile = 0;
49775       if (!file) {
49776         CImg<charT> body(1024);
49777         const char *const ext = cimg::split_filename(filename,body);
49778         if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file.
49779           nfile_header = cimg::fopen(filename,"rb");
49780           cimg_sprintf(body._data + std::strlen(body),".img");
49781           nfile = cimg::fopen(body,"rb");
49782         } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file.
49783           nfile = cimg::fopen(filename,"rb");
49784           cimg_sprintf(body._data + std::strlen(body),".hdr");
49785           nfile_header = cimg::fopen(body,"rb");
49786         } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file.
49787       } else nfile_header = nfile = file; // File is a Niftii file.
49788       if (!nfile || !nfile_header)
49789         throw CImgIOException(_cimg_instance
49790                               "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
49791                               cimg_instance,
49792                               filename?filename:"(FILE*)");
49793 
49794       // Read header.
49795       bool endian = false;
49796       unsigned int header_size;
49797       cimg::fread(&header_size,1,nfile_header);
49798       if (!header_size)
49799         throw CImgIOException(_cimg_instance
49800                               "load_analyze(): Invalid zero-size header in file '%s'.",
49801                               cimg_instance,
49802                               filename?filename:"(FILE*)");
49803       if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
49804 
49805       unsigned char *const header = new unsigned char[header_size];
49806       cimg::fread(header + 4,header_size - 4,nfile_header);
49807       if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
49808       if (endian) {
49809         cimg::invert_endianness((short*)(header + 40),5);
49810         cimg::invert_endianness((short*)(header + 70),1);
49811         cimg::invert_endianness((short*)(header + 72),1);
49812         cimg::invert_endianness((float*)(header + 76),4);
49813         cimg::invert_endianness((float*)(header + 108),1);
49814         cimg::invert_endianness((float*)(header + 112),1);
49815       }
49816 
49817       if (nfile_header==nfile) {
49818         const unsigned int vox_offset = (unsigned int)*(float*)(header + 108);
49819         std::fseek(nfile,vox_offset,SEEK_SET);
49820       }
49821 
49822       unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
49823       if (!dim[0])
49824         cimg::warn(_cimg_instance
49825                    "load_analyze(): File '%s' defines an image with zero dimensions.",
49826                    cimg_instance,
49827                    filename?filename:"(FILE*)");
49828 
49829       if (dim[0]>4)
49830         cimg::warn(_cimg_instance
49831                    "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
49832                    cimg_instance,
49833                    filename?filename:"(FILE*)",dim[0]);
49834 
49835       if (dim[0]>=1) dimx = dim[1];
49836       if (dim[0]>=2) dimy = dim[2];
49837       if (dim[0]>=3) dimz = dim[3];
49838       if (dim[0]>=4) dimv = dim[4];
49839       float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1;
49840       const unsigned short datatype = *(unsigned short*)(header + 70);
49841       if (voxel_size) {
49842         const float *vsize = (float*)(header + 76);
49843         voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
49844       }
49845       delete[] header;
49846 
49847       // Read pixel data.
49848       assign(dimx,dimy,dimz,dimv);
49849       const size_t pdim = (size_t)dimx*dimy*dimz*dimv;
49850       switch (datatype) {
49851       case 2 : {
49852         unsigned char *const buffer = new unsigned char[pdim];
49853         cimg::fread(buffer,pdim,nfile);
49854         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49855         delete[] buffer;
49856       } break;
49857       case 4 : {
49858         short *const buffer = new short[pdim];
49859         cimg::fread(buffer,pdim,nfile);
49860         if (endian) cimg::invert_endianness(buffer,pdim);
49861         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49862         delete[] buffer;
49863       } break;
49864       case 8 : {
49865         int *const buffer = new int[pdim];
49866         cimg::fread(buffer,pdim,nfile);
49867         if (endian) cimg::invert_endianness(buffer,pdim);
49868         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49869         delete[] buffer;
49870       } break;
49871       case 16 : {
49872         float *const buffer = new float[pdim];
49873         cimg::fread(buffer,pdim,nfile);
49874         if (endian) cimg::invert_endianness(buffer,pdim);
49875         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49876         delete[] buffer;
49877       } break;
49878       case 64 : {
49879         double *const buffer = new double[pdim];
49880         cimg::fread(buffer,pdim,nfile);
49881         if (endian) cimg::invert_endianness(buffer,pdim);
49882         cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
49883         delete[] buffer;
49884       } break;
49885       default :
49886         if (!file) cimg::fclose(nfile);
49887         throw CImgIOException(_cimg_instance
49888                               "load_analyze(): Unable to load datatype %d in file '%s'",
49889                               cimg_instance,
49890                               datatype,filename?filename:"(FILE*)");
49891       }
49892       if (!file) cimg::fclose(nfile);
49893       return *this;
49894     }
49895 
49896     //! Load image from a .cimg[z] file.
49897     /**
49898       \param filename Filename, as a C-string.
49899       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
49900       \param align Appending alignment.
49901     **/
49902     CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
49903       CImgList<T> list;
49904       list.load_cimg(filename);
49905       if (list._width==1) return list[0].move_to(*this);
49906       return assign(list.get_append(axis,align));
49907     }
49908 
49909     //! Load image from a .cimg[z] file \newinstance
49910     static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
49911       return CImg<T>().load_cimg(filename,axis,align);
49912     }
49913 
49914     //! Load image from a .cimg[z] file \overloading.
49915     CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
49916       CImgList<T> list;
49917       list.load_cimg(file);
49918       if (list._width==1) return list[0].move_to(*this);
49919       return assign(list.get_append(axis,align));
49920     }
49921 
49922     //! Load image from a .cimg[z] file \newinstance
49923     static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
49924       return CImg<T>().load_cimg(file,axis,align);
49925     }
49926 
49927     //! Load sub-images of a .cimg file.
49928     /**
49929       \param filename Filename, as a C-string.
49930       \param n0 Starting frame.
49931       \param n1 Ending frame (~0U for max).
49932       \param x0 X-coordinate of the starting sub-image vertex.
49933       \param y0 Y-coordinate of the starting sub-image vertex.
49934       \param z0 Z-coordinate of the starting sub-image vertex.
49935       \param c0 C-coordinate of the starting sub-image vertex.
49936       \param x1 X-coordinate of the ending sub-image vertex (~0U for max).
49937       \param y1 Y-coordinate of the ending sub-image vertex (~0U for max).
49938       \param z1 Z-coordinate of the ending sub-image vertex (~0U for max).
49939       \param c1 C-coordinate of the ending sub-image vertex (~0U for max).
49940       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
49941       \param align Appending alignment.
49942     **/
49943     CImg<T>& load_cimg(const char *const filename,
49944                        const unsigned int n0, const unsigned int n1,
49945                        const unsigned int x0, const unsigned int y0,
49946                        const unsigned int z0, const unsigned int c0,
49947                        const unsigned int x1, const unsigned int y1,
49948                        const unsigned int z1, const unsigned int c1,
49949                        const char axis='z', const float align=0) {
49950       CImgList<T> list;
49951       list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
49952       if (list._width==1) return list[0].move_to(*this);
49953       return assign(list.get_append(axis,align));
49954     }
49955 
49956     //! Load sub-images of a .cimg file \newinstance.
49957     static CImg<T> get_load_cimg(const char *const filename,
49958                                  const unsigned int n0, const unsigned int n1,
49959                                  const unsigned int x0, const unsigned int y0,
49960                                  const unsigned int z0, const unsigned int c0,
49961                                  const unsigned int x1, const unsigned int y1,
49962                                  const unsigned int z1, const unsigned int c1,
49963                                  const char axis='z', const float align=0) {
49964       return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
49965     }
49966 
49967     //! Load sub-images of a .cimg file \overloading.
49968     CImg<T>& load_cimg(std::FILE *const file,
49969                        const unsigned int n0, const unsigned int n1,
49970                        const unsigned int x0, const unsigned int y0,
49971                        const unsigned int z0, const unsigned int c0,
49972                        const unsigned int x1, const unsigned int y1,
49973                        const unsigned int z1, const unsigned int c1,
49974                        const char axis='z', const float align=0) {
49975       CImgList<T> list;
49976       list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
49977       if (list._width==1) return list[0].move_to(*this);
49978       return assign(list.get_append(axis,align));
49979     }
49980 
49981     //! Load sub-images of a .cimg file \newinstance.
49982     static CImg<T> get_load_cimg(std::FILE *const file,
49983                                  const unsigned int n0, const unsigned int n1,
49984                                  const unsigned int x0, const unsigned int y0,
49985                                  const unsigned int z0, const unsigned int c0,
49986                                  const unsigned int x1, const unsigned int y1,
49987                                  const unsigned int z1, const unsigned int c1,
49988                                  const char axis='z', const float align=0) {
49989       return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
49990     }
49991 
49992     //! Load image from an INRIMAGE-4 file.
49993     /**
49994        \param filename Filename, as a C-string.
49995        \param[out] voxel_size Pointer to the three voxel sizes read from the file.
49996     **/
49997     CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
49998       return _load_inr(0,filename,voxel_size);
49999     }
50000 
50001     //! Load image from an INRIMAGE-4 file \newinstance.
50002     static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
50003       return CImg<T>().load_inr(filename,voxel_size);
50004     }
50005 
50006     //! Load image from an INRIMAGE-4 file \overloading.
50007     CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
50008       return _load_inr(file,0,voxel_size);
50009     }
50010 
50011     //! Load image from an INRIMAGE-4 file \newinstance.
50012     static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
50013       return CImg<T>().load_inr(file,voxel_size);
50014     }
50015 
50016     static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
50017       CImg<charT> item(1024), tmp1(64), tmp2(64);
50018       *item = *tmp1 = *tmp2 = 0;
50019       out[0] = std::fscanf(file,"%63s",item._data);
50020       out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
50021       if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
50022         throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
50023                               pixel_type());
50024 
50025       while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) {
50026         cimg_sscanf(item," XDIM%*[^0-9]%d",out);
50027         cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1);
50028         cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2);
50029         cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3);
50030         cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6);
50031         if (voxel_size) {
50032           cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
50033           cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1);
50034           cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2);
50035         }
50036         if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1;
50037         switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) {
50038         case 0 : break;
50039         case 2 :
50040           out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0;
50041           std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough
50042         case 1 :
50043           if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5))  out[4] = 0;
50044           if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
50045           if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
50046           if (out[4]>=0) break; // fallthrough
50047         default :
50048           throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
50049                                 pixel_type(),
50050                                 tmp2._data);
50051         }
50052       }
50053       if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
50054         throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
50055                               pixel_type(),
50056                               out[0],out[1],out[2],out[3]);
50057       if (out[4]<0 || out[5]<0)
50058         throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
50059                               pixel_type());
50060       if (out[6]<0)
50061         throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
50062                               pixel_type());
50063       if (out[7]<0)
50064         throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
50065                               pixel_type());
50066     }
50067 
50068     CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
50069 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
50070      if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
50071         Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \
50072         cimg_forYZ(*this,y,z) { \
50073             cimg::fread(val,fopt[0]*fopt[3],nfile); \
50074             if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
50075             xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
50076           } \
50077         delete[] val; \
50078         loaded = true; \
50079       }
50080 
50081       if (!file && !filename)
50082         throw CImgArgumentException(_cimg_instance
50083                                     "load_inr(): Specified filename is (null).",
50084                                     cimg_instance);
50085 
50086       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
50087       int fopt[8], endian = cimg::endianness()?1:0;
50088       bool loaded = false;
50089       if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
50090       _load_inr_header(nfile,fopt,voxel_size);
50091       assign(fopt[0],fopt[1],fopt[2],fopt[3]);
50092       _cimg_load_inr_case(0,0,8,unsigned char);
50093       _cimg_load_inr_case(0,1,8,char);
50094       _cimg_load_inr_case(0,0,16,unsigned short);
50095       _cimg_load_inr_case(0,1,16,short);
50096       _cimg_load_inr_case(0,0,32,unsigned int);
50097       _cimg_load_inr_case(0,1,32,int);
50098       _cimg_load_inr_case(1,0,32,float);
50099       _cimg_load_inr_case(1,1,32,float);
50100       _cimg_load_inr_case(1,0,64,double);
50101       _cimg_load_inr_case(1,1,64,double);
50102       if (!loaded) {
50103         if (!file) cimg::fclose(nfile);
50104         throw CImgIOException(_cimg_instance
50105                               "load_inr(): Unknown pixel type defined in file '%s'.",
50106                               cimg_instance,
50107                               filename?filename:"(FILE*)");
50108       }
50109       if (!file) cimg::fclose(nfile);
50110       return *this;
50111     }
50112 
50113     //! Load image from a EXR file.
50114     /**
50115       \param filename Filename, as a C-string.
50116     **/
50117     CImg<T>& load_exr(const char *const filename) {
50118       if (!filename)
50119         throw CImgArgumentException(_cimg_instance
50120                                     "load_exr(): Specified filename is (null).",
50121                                     cimg_instance);
50122 #if defined(cimg_use_openexr)
50123       Imf::RgbaInputFile file(filename);
50124       Imath::Box2i dw = file.dataWindow();
50125       const int
50126         inwidth = dw.max.x - dw.min.x + 1,
50127         inheight = dw.max.y - dw.min.y + 1;
50128       Imf::Array2D<Imf::Rgba> pixels;
50129       pixels.resizeErase(inheight,inwidth);
50130       file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
50131       file.readPixels(dw.min.y, dw.max.y);
50132       assign(inwidth,inheight,1,4);
50133       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);
50134       cimg_forXY(*this,x,y) {
50135         *(ptr_r++) = (T)pixels[y][x].r;
50136         *(ptr_g++) = (T)pixels[y][x].g;
50137         *(ptr_b++) = (T)pixels[y][x].b;
50138         *(ptr_a++) = (T)pixels[y][x].a;
50139       }
50140 #elif defined(cimg_use_tinyexr)
50141       float *res;
50142       const char *err = 0;
50143       int width = 0, height = 0;
50144       const int ret = LoadEXR(&res,&width,&height,filename,&err);
50145       if (ret) throw CImgIOException(_cimg_instance
50146                                      "load_exr(): Unable to load EXR file '%s'.",
50147                                      cimg_instance,filename);
50148       CImg<floatT>(out,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this);
50149       std::free(res);
50150 #else
50151       return load_other(filename);
50152 #endif
50153       return *this;
50154     }
50155 
50156     //! Load image from a EXR file \newinstance.
50157     static CImg<T> get_load_exr(const char *const filename) {
50158       return CImg<T>().load_exr(filename);
50159     }
50160 
50161     //! Load image from a PANDORE-5 file.
50162     /**
50163       \param filename Filename, as a C-string.
50164     **/
50165     CImg<T>& load_pandore(const char *const filename) {
50166       return _load_pandore(0,filename);
50167     }
50168 
50169     //! Load image from a PANDORE-5 file \newinstance.
50170     static CImg<T> get_load_pandore(const char *const filename) {
50171       return CImg<T>().load_pandore(filename);
50172     }
50173 
50174     //! Load image from a PANDORE-5 file \overloading.
50175     CImg<T>& load_pandore(std::FILE *const file) {
50176       return _load_pandore(file,0);
50177     }
50178 
50179     //! Load image from a PANDORE-5 file \newinstance.
50180     static CImg<T> get_load_pandore(std::FILE *const file) {
50181       return CImg<T>().load_pandore(file);
50182     }
50183 
50184     CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
50185 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
50186         cimg::fread(dims,nbdim,nfile); \
50187         if (endian) cimg::invert_endianness(dims,nbdim); \
50188         assign(nwidth,nheight,ndepth,ndim); \
50189         const size_t siz = size(); \
50190         stype *buffer = new stype[siz]; \
50191         cimg::fread(buffer,siz,nfile); \
50192         if (endian) cimg::invert_endianness(buffer,siz); \
50193         T *ptrd = _data; \
50194         cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
50195         buffer-=siz; \
50196         delete[] buffer
50197 
50198 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
50199         if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
50200         else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
50201         else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
50202         else throw CImgIOException(_cimg_instance \
50203                                    "load_pandore(): Unknown pixel datatype in file '%s'.", \
50204                                    cimg_instance, \
50205                                    filename?filename:"(FILE*)"); }
50206       if (!file && !filename)
50207         throw CImgArgumentException(_cimg_instance
50208                                     "load_pandore(): Specified filename is (null).",
50209                                     cimg_instance);
50210 
50211       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
50212       CImg<charT> header(32);
50213       cimg::fread(header._data,12,nfile);
50214       if (cimg::strncasecmp("PANDORE",header,7)) {
50215         if (!file) cimg::fclose(nfile);
50216         throw CImgIOException(_cimg_instance
50217                               "load_pandore(): PANDORE header not found in file '%s'.",
50218                               cimg_instance,
50219                               filename?filename:"(FILE*)");
50220       }
50221       unsigned int imageid, dims[8] = { 0 };
50222       int ptbuf[4] = { 0 };
50223       cimg::fread(&imageid,1,nfile);
50224       const bool endian = imageid>255;
50225       if (endian) cimg::invert_endianness(imageid);
50226       cimg::fread(header._data,20,nfile);
50227 
50228       switch (imageid) {
50229       case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
50230       case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
50231       case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
50232       case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
50233       case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
50234       case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
50235       case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
50236       case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
50237       case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
50238       case 11 : { // Region 1d
50239         cimg::fread(dims,3,nfile);
50240         if (endian) cimg::invert_endianness(dims,3);
50241         assign(dims[1],1,1,1);
50242         const unsigned siz = size();
50243         if (dims[2]<256) {
50244           unsigned char *buffer = new unsigned char[siz];
50245           cimg::fread(buffer,siz,nfile);
50246           T *ptrd = _data;
50247           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50248           buffer-=siz;
50249           delete[] buffer;
50250         } else {
50251           if (dims[2]<65536) {
50252             unsigned short *buffer = new unsigned short[siz];
50253             cimg::fread(buffer,siz,nfile);
50254             if (endian) cimg::invert_endianness(buffer,siz);
50255             T *ptrd = _data;
50256             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50257             buffer-=siz;
50258             delete[] buffer;
50259           } else {
50260             unsigned int *buffer = new unsigned int[siz];
50261             cimg::fread(buffer,siz,nfile);
50262             if (endian) cimg::invert_endianness(buffer,siz);
50263             T *ptrd = _data;
50264             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50265             buffer-=siz;
50266             delete[] buffer;
50267           }
50268         }
50269       }
50270         break;
50271       case 12 : { // Region 2d
50272         cimg::fread(dims,4,nfile);
50273         if (endian) cimg::invert_endianness(dims,4);
50274         assign(dims[2],dims[1],1,1);
50275         const size_t siz = size();
50276         if (dims[3]<256) {
50277           unsigned char *buffer = new unsigned char[siz];
50278           cimg::fread(buffer,siz,nfile);
50279           T *ptrd = _data;
50280           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50281           buffer-=siz;
50282           delete[] buffer;
50283         } else {
50284           if (dims[3]<65536) {
50285             unsigned short *buffer = new unsigned short[siz];
50286             cimg::fread(buffer,siz,nfile);
50287             if (endian) cimg::invert_endianness(buffer,siz);
50288             T *ptrd = _data;
50289             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50290             buffer-=siz;
50291             delete[] buffer;
50292           } else {
50293             unsigned int *buffer = new unsigned int[siz];
50294             cimg::fread(buffer,siz,nfile);
50295             if (endian) cimg::invert_endianness(buffer,siz);
50296             T *ptrd = _data;
50297             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50298             buffer-=siz;
50299             delete[] buffer;
50300           }
50301         }
50302       }
50303         break;
50304       case 13 : { // Region 3d
50305         cimg::fread(dims,5,nfile);
50306         if (endian) cimg::invert_endianness(dims,5);
50307         assign(dims[3],dims[2],dims[1],1);
50308         const size_t siz = size();
50309         if (dims[4]<256) {
50310           unsigned char *buffer = new unsigned char[siz];
50311           cimg::fread(buffer,siz,nfile);
50312           T *ptrd = _data;
50313           cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50314           buffer-=siz;
50315           delete[] buffer;
50316         } else {
50317           if (dims[4]<65536) {
50318             unsigned short *buffer = new unsigned short[siz];
50319             cimg::fread(buffer,siz,nfile);
50320             if (endian) cimg::invert_endianness(buffer,siz);
50321             T *ptrd = _data;
50322             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50323             buffer-=siz;
50324             delete[] buffer;
50325           } else {
50326             unsigned int *buffer = new unsigned int[siz];
50327             cimg::fread(buffer,siz,nfile);
50328             if (endian) cimg::invert_endianness(buffer,siz);
50329             T *ptrd = _data;
50330             cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
50331             buffer-=siz;
50332             delete[] buffer;
50333           }
50334         }
50335       }
50336         break;
50337       case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
50338       case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
50339       case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
50340       case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
50341       case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
50342       case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
50343       case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
50344       case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break;
50345       case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
50346       case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
50347       case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
50348       case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
50349       case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
50350       case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
50351       case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1);
50352         break;
50353       case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
50354       case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4);
50355         break;
50356       case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
50357       case 34 : { // Points 1d
50358         cimg::fread(ptbuf,1,nfile);
50359         if (endian) cimg::invert_endianness(ptbuf,1);
50360         assign(1); (*this)(0) = (T)ptbuf[0];
50361       } break;
50362       case 35 : { // Points 2d
50363         cimg::fread(ptbuf,2,nfile);
50364         if (endian) cimg::invert_endianness(ptbuf,2);
50365         assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
50366       } break;
50367       case 36 : { // Points 3d
50368         cimg::fread(ptbuf,3,nfile);
50369         if (endian) cimg::invert_endianness(ptbuf,3);
50370         assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
50371       } break;
50372       default :
50373         if (!file) cimg::fclose(nfile);
50374         throw CImgIOException(_cimg_instance
50375                               "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
50376                               cimg_instance,
50377                               imageid,filename?filename:"(FILE*)");
50378       }
50379       if (!file) cimg::fclose(nfile);
50380       return *this;
50381     }
50382 
50383     //! Load image from a PAR-REC (Philips) file.
50384     /**
50385       \param filename Filename, as a C-string.
50386       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
50387       \param align Appending alignment.
50388     **/
50389     CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
50390       CImgList<T> list;
50391       list.load_parrec(filename);
50392       if (list._width==1) return list[0].move_to(*this);
50393       return assign(list.get_append(axis,align));
50394     }
50395 
50396     //! Load image from a PAR-REC (Philips) file \newinstance.
50397     static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
50398       return CImg<T>().load_parrec(filename,axis,align);
50399     }
50400 
50401     //! Load image from a raw binary file.
50402     /**
50403       \param filename Filename, as a C-string.
50404       \param size_x Width of the image buffer.
50405       \param size_y Height of the image buffer.
50406       \param size_z Depth of the image buffer.
50407       \param size_c Spectrum of the image buffer.
50408       \param is_multiplexed Tells if the image values are multiplexed along the C-axis.
50409       \param invert_endianness Tells if the endianness of the image buffer must be inverted.
50410       \param offset Starting offset of the read in the specified file.
50411     **/
50412     CImg<T>& load_raw(const char *const filename,
50413                       const unsigned int size_x=0, const unsigned int size_y=1,
50414                       const unsigned int size_z=1, const unsigned int size_c=1,
50415                       const bool is_multiplexed=false, const bool invert_endianness=false,
50416                       const ulongT offset=0) {
50417       return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
50418     }
50419 
50420     //! Load image from a raw binary file \newinstance.
50421     static CImg<T> get_load_raw(const char *const filename,
50422                                 const unsigned int size_x=0, const unsigned int size_y=1,
50423                                 const unsigned int size_z=1, const unsigned int size_c=1,
50424                                 const bool is_multiplexed=false, const bool invert_endianness=false,
50425                                 const ulongT offset=0) {
50426       return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
50427     }
50428 
50429     //! Load image from a raw binary file \overloading.
50430     CImg<T>& load_raw(std::FILE *const file,
50431                       const unsigned int size_x=0, const unsigned int size_y=1,
50432                       const unsigned int size_z=1, const unsigned int size_c=1,
50433                       const bool is_multiplexed=false, const bool invert_endianness=false,
50434                       const ulongT offset=0) {
50435       return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
50436     }
50437 
50438     //! Load image from a raw binary file \newinstance.
50439     static CImg<T> get_load_raw(std::FILE *const file,
50440                                 const unsigned int size_x=0, const unsigned int size_y=1,
50441                                 const unsigned int size_z=1, const unsigned int size_c=1,
50442                                 const bool is_multiplexed=false, const bool invert_endianness=false,
50443                                 const ulongT offset=0) {
50444       return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
50445     }
50446 
50447     CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
50448 		       const unsigned int size_x, const unsigned int size_y,
50449 		       const unsigned int size_z, const unsigned int size_c,
50450 		       const bool is_multiplexed, const bool invert_endianness,
50451                        const ulongT offset) {
50452       if (!file && !filename)
50453         throw CImgArgumentException(_cimg_instance
50454                                     "load_raw(): Specified filename is (null).",
50455                                     cimg_instance);
50456       if (cimg::is_directory(filename))
50457         throw CImgArgumentException(_cimg_instance
50458                                     "load_raw(): Specified filename '%s' is a directory.",
50459                                     cimg_instance,filename);
50460 
50461       ulongT siz = (ulongT)size_x*size_y*size_z*size_c;
50462       unsigned int
50463         _size_x = size_x,
50464         _size_y = size_y,
50465         _size_z = size_z,
50466         _size_c = size_c;
50467       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
50468       if (!siz) {  // Retrieve file size.
50469         const longT fpos = cimg::ftell(nfile);
50470         if (fpos<0) throw CImgArgumentException(_cimg_instance
50471                                                 "load_raw(): Cannot determine size of input file '%s'.",
50472                                                 cimg_instance,filename?filename:"(FILE*)");
50473         cimg::fseek(nfile,0,SEEK_END);
50474         siz = cimg::ftell(nfile)/sizeof(T);
50475 		_size_y = (unsigned int)siz;
50476         _size_x = _size_z = _size_c = 1;
50477         cimg::fseek(nfile,fpos,SEEK_SET);
50478       }
50479       cimg::fseek(nfile,offset,SEEK_SET);
50480       assign(_size_x,_size_y,_size_z,_size_c,0);
50481       if (siz && (!is_multiplexed || size_c==1)) {
50482         cimg::fread(_data,siz,nfile);
50483         if (invert_endianness) cimg::invert_endianness(_data,siz);
50484       } else if (siz) {
50485         CImg<T> buf(1,1,1,_size_c);
50486         cimg_forXYZ(*this,x,y,z) {
50487           cimg::fread(buf._data,_size_c,nfile);
50488           if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
50489           set_vector_at(buf,x,y,z);
50490         }
50491       }
50492       if (!file) cimg::fclose(nfile);
50493       return *this;
50494     }
50495 
50496     //! Load image sequence from a YUV file.
50497     /**
50498       \param filename Filename, as a C-string.
50499       \param size_x Width of the frames.
50500       \param size_y Height of the frames.
50501       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
50502       \param first_frame Index of the first frame to read.
50503       \param last_frame Index of the last frame to read.
50504       \param step_frame Step value for frame reading.
50505       \param yuv2rgb Tells if the YUV to RGB transform must be applied.
50506       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
50507     **/
50508     CImg<T>& load_yuv(const char *const filename,
50509                       const unsigned int size_x, const unsigned int size_y=1,
50510                       const unsigned int chroma_subsampling=444,
50511                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50512                       const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
50513       return get_load_yuv(filename,size_x,size_y,chroma_subsampling,
50514                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
50515     }
50516 
50517     //! Load image sequence from a YUV file \newinstance.
50518     static CImg<T> get_load_yuv(const char *const filename,
50519                                 const unsigned int size_x, const unsigned int size_y=1,
50520                                 const unsigned int chroma_subsampling=444,
50521                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50522                                 const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
50523       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
50524                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
50525     }
50526 
50527     //! Load image sequence from a YUV file \overloading.
50528     CImg<T>& load_yuv(std::FILE *const file,
50529                       const unsigned int size_x, const unsigned int size_y=1,
50530                       const unsigned int chroma_subsampling=444,
50531                       const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50532 		      const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
50533       return get_load_yuv(file,size_x,size_y,chroma_subsampling,
50534                           first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
50535     }
50536 
50537     //! Load image sequence from a YUV file \newinstance.
50538     static CImg<T> get_load_yuv(std::FILE *const file,
50539                                 const unsigned int size_x, const unsigned int size_y=1,
50540                                 const unsigned int chroma_subsampling=444,
50541                                 const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50542 				const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
50543       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
50544                                     first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
50545     }
50546 
50547     //! Load 3d object from a .OFF file.
50548     /**
50549         \param[out] primitives Primitives data of the 3d object.
50550         \param[out] colors Colors data of the 3d object.
50551         \param filename Filename, as a C-string.
50552     **/
50553     template<typename tf, typename tc>
50554     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
50555       return _load_off(primitives,colors,0,filename);
50556     }
50557 
50558     //! Load 3d object from a .OFF file \newinstance.
50559     template<typename tf, typename tc>
50560     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
50561       return CImg<T>().load_off(primitives,colors,filename);
50562     }
50563 
50564     //! Load 3d object from a .OFF file \overloading.
50565     template<typename tf, typename tc>
50566     CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
50567       return _load_off(primitives,colors,file,0);
50568     }
50569 
50570     //! Load 3d object from a .OFF file \newinstance.
50571     template<typename tf, typename tc>
50572     static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
50573       return CImg<T>().load_off(primitives,colors,file);
50574     }
50575 
50576     template<typename tf, typename tc>
50577     CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
50578                        std::FILE *const file, const char *const filename) {
50579       if (!file && !filename)
50580         throw CImgArgumentException(_cimg_instance
50581                                     "load_off(): Specified filename is (null).",
50582                                     cimg_instance);
50583 
50584       std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
50585       unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
50586       CImg<charT> line(256); *line = 0;
50587       int err;
50588 
50589       // Skip comments, and read magic string OFF
50590       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
50591       if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
50592         if (!file) cimg::fclose(nfile);
50593         throw CImgIOException(_cimg_instance
50594                               "load_off(): OFF header not found in file '%s'.",
50595                               cimg_instance,
50596                               filename?filename:"(FILE*)");
50597       }
50598       do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
50599       if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
50600         if (!file) cimg::fclose(nfile);
50601         throw CImgIOException(_cimg_instance
50602                               "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
50603                               cimg_instance,
50604                               filename?filename:"(FILE*)");
50605       }
50606 
50607       // Read points data
50608       assign(nb_points,3);
50609       float X = 0, Y = 0, Z = 0;
50610       cimg_forX(*this,l) {
50611         do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
50612         if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
50613           if (!file) cimg::fclose(nfile);
50614           throw CImgIOException(_cimg_instance
50615                                 "load_off(): Failed to read vertex %u/%u in file '%s'.",
50616                                 cimg_instance,
50617                                 l + 1,nb_points,filename?filename:"(FILE*)");
50618         }
50619         (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
50620       }
50621 
50622       // Read primitive data
50623       primitives.assign();
50624       colors.assign();
50625       bool stop_flag = false;
50626       while (!stop_flag) {
50627         float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
50628         unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
50629         *line = 0;
50630         if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true;
50631         else {
50632           ++nb_read;
50633           switch (prim) {
50634           case 1 : {
50635             if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) {
50636               cimg::warn(_cimg_instance
50637                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50638                          cimg_instance,
50639                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50640 
50641               err = std::fscanf(nfile,"%*[^\n] ");
50642             } else {
50643               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50644               CImg<tf>::vector(i0).move_to(primitives);
50645               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
50646             }
50647           } break;
50648           case 2 : {
50649             if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) {
50650               cimg::warn(_cimg_instance
50651                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50652                          cimg_instance,
50653                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50654 
50655               err = std::fscanf(nfile,"%*[^\n] ");
50656             } else {
50657               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50658               CImg<tf>::vector(i0,i1).move_to(primitives);
50659               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
50660             }
50661           } break;
50662           case 3 : {
50663             if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) {
50664               cimg::warn(_cimg_instance
50665                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50666                          cimg_instance,
50667                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50668 
50669               err = std::fscanf(nfile,"%*[^\n] ");
50670             } else {
50671               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50672               CImg<tf>::vector(i0,i2,i1).move_to(primitives);
50673               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
50674             }
50675           } break;
50676           case 4 : {
50677             if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) {
50678               cimg::warn(_cimg_instance
50679                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50680                          cimg_instance,
50681                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50682 
50683               err = std::fscanf(nfile,"%*[^\n] ");
50684             } else {
50685               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50686               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
50687               CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
50688             }
50689           } break;
50690           case 5 : {
50691             if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) {
50692               cimg::warn(_cimg_instance
50693                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50694                          cimg_instance,
50695                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50696 
50697               err = std::fscanf(nfile,"%*[^\n] ");
50698             } else {
50699               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50700               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
50701               CImg<tf>::vector(i0,i4,i3).move_to(primitives);
50702               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
50703               ++nb_primitives;
50704             }
50705           } break;
50706           case 6 : {
50707             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) {
50708               cimg::warn(_cimg_instance
50709                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50710                          cimg_instance,
50711                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50712 
50713               err = std::fscanf(nfile,"%*[^\n] ");
50714             } else {
50715               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50716               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
50717               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
50718               colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
50719               ++nb_primitives;
50720             }
50721           } break;
50722           case 7 : {
50723             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) {
50724               cimg::warn(_cimg_instance
50725                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50726                          cimg_instance,
50727                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50728 
50729               err = std::fscanf(nfile,"%*[^\n] ");
50730             } else {
50731               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50732               CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
50733               CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
50734               CImg<tf>::vector(i3,i2,i1).move_to(primitives);
50735               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
50736               ++(++nb_primitives);
50737             }
50738           } break;
50739           case 8 : {
50740             if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line._data))<7) {
50741               cimg::warn(_cimg_instance
50742                          "load_off(): Failed to read primitive %u/%u from file '%s'.",
50743                          cimg_instance,
50744                          nb_read,nb_primitives,filename?filename:"(FILE*)");
50745 
50746               err = std::fscanf(nfile,"%*[^\n] ");
50747             } else {
50748               err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
50749               CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
50750               CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
50751               CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
50752               colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
50753               ++(++nb_primitives);
50754             }
50755           } break;
50756           default :
50757             cimg::warn(_cimg_instance
50758                        "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
50759                        cimg_instance,
50760                        nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
50761 
50762             err = std::fscanf(nfile,"%*[^\n] ");
50763           }
50764         }
50765       }
50766       if (!file) cimg::fclose(nfile);
50767       if (primitives._width!=nb_primitives)
50768         cimg::warn(_cimg_instance
50769                    "load_off(): Only %u/%u primitives read from file '%s'.",
50770                    cimg_instance,
50771                    primitives._width,nb_primitives,filename?filename:"(FILE*)");
50772       return *this;
50773     }
50774 
50775     //! Load image sequence from a video file, using OpenCV library.
50776     /**
50777       \param filename Filename, as a C-string.
50778       \param first_frame Index of the first frame to read.
50779       \param last_frame Index of the last frame to read.
50780       \param step_frame Step value for frame reading.
50781       \param axis Alignment axis.
50782       \param align Apending alignment.
50783     **/
50784     CImg<T>& load_video(const char *const filename,
50785                         const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50786                         const unsigned int step_frame=1,
50787                         const char axis='z', const float align=0) {
50788       return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this);
50789     }
50790 
50791     //! Load image sequence from a video file, using OpenCV library \newinstance.
50792     static CImg<T> get_load_video(const char *const filename,
50793                                   const unsigned int first_frame=0, const unsigned int last_frame=~0U,
50794                                   const unsigned int step_frame=1,
50795                                   const char axis='z', const float align=0) {
50796       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align);
50797     }
50798 
50799     //! Load image sequence using FFMPEG's external tool 'ffmpeg'.
50800     /**
50801       \param filename Filename, as a C-string.
50802       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
50803       \param align Appending alignment.
50804     **/
50805     CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
50806       return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
50807     }
50808 
50809     //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance.
50810     static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
50811       return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
50812     }
50813 
50814     //! Load gif file, using Imagemagick or GraphicsMagicks's external tools.
50815     /**
50816       \param filename Filename, as a C-string.
50817       \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
50818       \param align Appending alignment.
50819     **/
50820     CImg<T>& load_gif_external(const char *const filename,
50821                                const char axis='z', const float align=0) {
50822       return get_load_gif_external(filename,axis,align).move_to(*this);
50823     }
50824 
50825     //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance.
50826     static CImg<T> get_load_gif_external(const char *const filename,
50827                                          const char axis='z', const float align=0) {
50828       return CImgList<T>().load_gif_external(filename).get_append(axis,align);
50829     }
50830 
50831     //! Load image using GraphicsMagick's external tool 'gm'.
50832     /**
50833        \param filename Filename, as a C-string.
50834     **/
50835     CImg<T>& load_graphicsmagick_external(const char *const filename) {
50836       if (!filename)
50837         throw CImgArgumentException(_cimg_instance
50838                                     "load_graphicsmagick_external(): Specified filename is (null).",
50839                                     cimg_instance);
50840       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
50841       CImg<charT> command(1024), filename_tmp(256);
50842       std::FILE *file = 0;
50843       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
50844 #if cimg_OS==1
50845       if (!cimg::system("which gm")) {
50846         cimg_snprintf(command,command._width,"%s convert \"%s\" pnm:-",
50847                       cimg::graphicsmagick_path(),s_filename.data());
50848         file = popen(command,"r");
50849         if (file) {
50850           const unsigned int omode = cimg::exception_mode();
50851           cimg::exception_mode(0);
50852           try { load_pnm(file); } catch (...) {
50853             pclose(file);
50854             cimg::exception_mode(omode);
50855             throw CImgIOException(_cimg_instance
50856                                   "load_graphicsmagick_external(): Failed to load file '%s' "
50857                                   "with external command 'gm'.",
50858                                   cimg_instance,
50859                                   filename);
50860           }
50861           pclose(file);
50862           return *this;
50863         }
50864       }
50865 #endif
50866       do {
50867         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm",
50868                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
50869         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
50870       } while (file);
50871       cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s\"",
50872                     cimg::graphicsmagick_path(),s_filename.data(),
50873                     CImg<charT>::string(filename_tmp)._system_strescape().data());
50874       cimg::system(command,cimg::graphicsmagick_path());
50875       if (!(file = std_fopen(filename_tmp,"rb"))) {
50876         cimg::fclose(cimg::fopen(filename,"r"));
50877         throw CImgIOException(_cimg_instance
50878                               "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
50879                               cimg_instance,
50880                               filename);
50881 
50882       } else cimg::fclose(file);
50883       load_pnm(filename_tmp);
50884       std::remove(filename_tmp);
50885       return *this;
50886     }
50887 
50888     //! Load image using GraphicsMagick's external tool 'gm' \newinstance.
50889     static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
50890       return CImg<T>().load_graphicsmagick_external(filename);
50891     }
50892 
50893     //! Load gzipped image file, using external tool 'gunzip'.
50894     /**
50895        \param filename Filename, as a C-string.
50896     **/
50897     CImg<T>& load_gzip_external(const char *const filename) {
50898       if (!filename)
50899         throw CImgIOException(_cimg_instance
50900                               "load_gzip_external(): Specified filename is (null).",
50901                               cimg_instance);
50902       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
50903       CImg<charT> command(1024), filename_tmp(256), body(256);
50904       const char
50905         *const ext = cimg::split_filename(filename,body),
50906         *const ext2 = cimg::split_filename(body,0);
50907 
50908       std::FILE *file = 0;
50909       do {
50910         if (!cimg::strcasecmp(ext,"gz")) {
50911           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
50912                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
50913           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
50914                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
50915         } else {
50916           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
50917                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
50918           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
50919                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
50920         }
50921         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
50922       } while (file);
50923       cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
50924                     cimg::gunzip_path(),
50925                     CImg<charT>::string(filename)._system_strescape().data(),
50926                     CImg<charT>::string(filename_tmp)._system_strescape().data());
50927       cimg::system(command);
50928       if (!(file = std_fopen(filename_tmp,"rb"))) {
50929         cimg::fclose(cimg::fopen(filename,"r"));
50930         throw CImgIOException(_cimg_instance
50931                               "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
50932                               cimg_instance,
50933                               filename);
50934 
50935       } else cimg::fclose(file);
50936       load(filename_tmp);
50937       std::remove(filename_tmp);
50938       return *this;
50939     }
50940 
50941     //! Load gzipped image file, using external tool 'gunzip' \newinstance.
50942     static CImg<T> get_load_gzip_external(const char *const filename) {
50943       return CImg<T>().load_gzip_external(filename);
50944     }
50945 
50946     //! Load image using ImageMagick's external tool 'convert'.
50947     /**
50948        \param filename Filename, as a C-string.
50949     **/
50950     CImg<T>& load_imagemagick_external(const char *const filename) {
50951       if (!filename)
50952         throw CImgArgumentException(_cimg_instance
50953                                     "load_imagemagick_external(): Specified filename is (null).",
50954                                     cimg_instance);
50955       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
50956       CImg<charT> command(1024), filename_tmp(256);
50957       std::FILE *file = 0;
50958       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
50959 #if cimg_OS==1
50960       if (!cimg::system("which convert")) {
50961         cimg_snprintf(command,command._width,"%s%s \"%s\" pnm:-",
50962                       cimg::imagemagick_path(),
50963                       !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
50964                       s_filename.data());
50965         file = popen(command,"r");
50966         if (file) {
50967           const unsigned int omode = cimg::exception_mode();
50968           cimg::exception_mode(0);
50969           try { load_pnm(file); } catch (...) {
50970             pclose(file);
50971             cimg::exception_mode(omode);
50972             throw CImgIOException(_cimg_instance
50973                                   "load_imagemagick_external(): Failed to load file '%s' with "
50974                                   "external command 'magick/convert'.",
50975                                   cimg_instance,
50976                                   filename);
50977           }
50978           pclose(file);
50979           return *this;
50980         }
50981       }
50982 #endif
50983       do {
50984         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.pnm",
50985                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
50986         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
50987       } while (file);
50988       cimg_snprintf(command,command._width,"%s%s \"%s\" \"%s\"",
50989                     cimg::imagemagick_path(),
50990                     !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
50991                     s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
50992       cimg::system(command,cimg::imagemagick_path());
50993       if (!(file = std_fopen(filename_tmp,"rb"))) {
50994         cimg::fclose(cimg::fopen(filename,"r"));
50995         throw CImgIOException(_cimg_instance
50996                               "load_imagemagick_external(): Failed to load file '%s' with "
50997                               "external command 'magick/convert'.",
50998                               cimg_instance,
50999                               filename);
51000 
51001       } else cimg::fclose(file);
51002       load_pnm(filename_tmp);
51003       std::remove(filename_tmp);
51004       return *this;
51005     }
51006 
51007     //! Load image using ImageMagick's external tool 'convert' \newinstance.
51008     static CImg<T> get_load_imagemagick_external(const char *const filename) {
51009       return CImg<T>().load_imagemagick_external(filename);
51010     }
51011 
51012     //! Load image from a DICOM file, using XMedcon's external tool 'medcon'.
51013     /**
51014        \param filename Filename, as a C-string.
51015     **/
51016     CImg<T>& load_medcon_external(const char *const filename) {
51017       if (!filename)
51018         throw CImgArgumentException(_cimg_instance
51019                                     "load_medcon_external(): Specified filename is (null).",
51020                                     cimg_instance);
51021       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
51022       CImg<charT> command(1024), filename_tmp(256), body(256);
51023       cimg::fclose(cimg::fopen(filename,"r"));
51024       std::FILE *file = 0;
51025       do {
51026         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
51027         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
51028       } while (file);
51029       cimg_snprintf(command,command._width,"%s -w -c anlz -o \"%s\" -f \"%s\"",
51030                     cimg::medcon_path(),
51031                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
51032                     CImg<charT>::string(filename)._system_strescape().data());
51033       cimg::system(command);
51034       cimg::split_filename(filename_tmp,body);
51035 
51036       cimg_snprintf(command,command._width,"%s.hdr",body._data);
51037       file = std_fopen(command,"rb");
51038       if (!file) {
51039         cimg_snprintf(command,command._width,"m000-%s.hdr",body._data);
51040         file = std_fopen(command,"rb");
51041         if (!file) {
51042           throw CImgIOException(_cimg_instance
51043                                 "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
51044                                 cimg_instance,
51045                                 filename);
51046         }
51047       }
51048       cimg::fclose(file);
51049       load_analyze(command);
51050       std::remove(command);
51051       cimg::split_filename(command,body);
51052       cimg_snprintf(command,command._width,"%s.img",body._data);
51053       std::remove(command);
51054       return *this;
51055     }
51056 
51057     //! Load image from a DICOM file, using XMedcon's external tool 'medcon' \newinstance.
51058     static CImg<T> get_load_medcon_external(const char *const filename) {
51059       return CImg<T>().load_medcon_external(filename);
51060     }
51061 
51062     //! Load image from a RAW Color Camera file, using external tool 'dcraw'.
51063     /**
51064        \param filename Filename, as a C-string.
51065     **/
51066     CImg<T>& load_dcraw_external(const char *const filename) {
51067       if (!filename)
51068         throw CImgArgumentException(_cimg_instance
51069                                     "load_dcraw_external(): Specified filename is (null).",
51070                                     cimg_instance);
51071       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
51072       CImg<charT> command(1024), filename_tmp(256);
51073       std::FILE *file = 0;
51074       const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
51075 #if cimg_OS==1
51076       cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"",
51077                     cimg::dcraw_path(),s_filename.data());
51078       file = popen(command,"r");
51079       if (file) {
51080         const unsigned int omode = cimg::exception_mode();
51081         cimg::exception_mode(0);
51082         try { load_pnm(file); } catch (...) {
51083           pclose(file);
51084           cimg::exception_mode(omode);
51085           throw CImgIOException(_cimg_instance
51086                                 "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
51087                                 cimg_instance,
51088                                 filename);
51089         }
51090         pclose(file);
51091         return *this;
51092       }
51093 #endif
51094       do {
51095         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
51096                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
51097         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
51098       } while (file);
51099       cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\" > \"%s\"",
51100                     cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
51101       cimg::system(command,cimg::dcraw_path());
51102       if (!(file = std_fopen(filename_tmp,"rb"))) {
51103         cimg::fclose(cimg::fopen(filename,"r"));
51104         throw CImgIOException(_cimg_instance
51105                               "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
51106                               cimg_instance,
51107                               filename);
51108 
51109       } else cimg::fclose(file);
51110       load_pnm(filename_tmp);
51111       std::remove(filename_tmp);
51112       return *this;
51113     }
51114 
51115     //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance.
51116     static CImg<T> get_load_dcraw_external(const char *const filename) {
51117       return CImg<T>().load_dcraw_external(filename);
51118     }
51119 
51120     //! Load image from a camera stream, using OpenCV.
51121     /**
51122        \param camera_index Index of the camera to capture images from.
51123        \param skip_frames Number of frames to skip before the capture.
51124        \param release_camera Tells if the camera ressource must be released at the end of the method.
51125        \param capture_width Width of the desired image.
51126        \param capture_height Height of the desired image.
51127     **/
51128     CImg<T>& load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0,
51129                          const bool release_camera=true, const unsigned int capture_width=0,
51130                          const unsigned int capture_height=0) {
51131 #ifdef cimg_use_opencv
51132       if (camera_index>99)
51133         throw CImgArgumentException(_cimg_instance
51134                                     "load_camera(): Invalid request for camera #%u "
51135                                     "(no more than 100 cameras can be managed simultaneously).",
51136                                     cimg_instance,
51137                                     camera_index);
51138       static CvCapture *capture[100] = { 0 };
51139       static unsigned int capture_w[100], capture_h[100];
51140       if (release_camera) {
51141         cimg::mutex(9);
51142         if (capture[camera_index]) cvReleaseCapture(&(capture[camera_index]));
51143         capture[camera_index] = 0;
51144         capture_w[camera_index] = capture_h[camera_index] = 0;
51145         cimg::mutex(9,0);
51146         return *this;
51147       }
51148       if (!capture[camera_index]) {
51149         cimg::mutex(9);
51150         capture[camera_index] = cvCreateCameraCapture(camera_index);
51151         capture_w[camera_index] = 0;
51152         capture_h[camera_index] = 0;
51153         cimg::mutex(9,0);
51154         if (!capture[camera_index]) {
51155           throw CImgIOException(_cimg_instance
51156                                 "load_camera(): Failed to initialize camera #%u.",
51157                                 cimg_instance,
51158                                 camera_index);
51159         }
51160       }
51161       cimg::mutex(9);
51162       if (capture_width!=capture_w[camera_index]) {
51163         cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_WIDTH,capture_width);
51164         capture_w[camera_index] = capture_width;
51165       }
51166       if (capture_height!=capture_h[camera_index]) {
51167         cvSetCaptureProperty(capture[camera_index],CV_CAP_PROP_FRAME_HEIGHT,capture_height);
51168         capture_h[camera_index] = capture_height;
51169       }
51170       const IplImage *img = 0;
51171       for (unsigned int i = 0; i<skip_frames; ++i) img = cvQueryFrame(capture[camera_index]);
51172       img = cvQueryFrame(capture[camera_index]);
51173       if (img) {
51174         const int step = (int)(img->widthStep - 3*img->width);
51175         assign(img->width,img->height,1,3);
51176         const unsigned char* ptrs = (unsigned char*)img->imageData;
51177         T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
51178         if (step>0) cimg_forY(*this,y) {
51179             cimg_forX(*this,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
51180             ptrs+=step;
51181           } else for (ulongT siz = (ulongT)img->width*img->height; siz; --siz) {
51182             *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++);
51183           }
51184       }
51185       cimg::mutex(9,0);
51186       return *this;
51187 #else
51188       cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height);
51189       throw CImgIOException(_cimg_instance
51190                             "load_camera(): This function requires the OpenCV library to run "
51191                             "(macro 'cimg_use_opencv' must be defined).",
51192                             cimg_instance);
51193 #endif
51194     }
51195 
51196     //! Load image from a camera stream, using OpenCV \newinstance.
51197     static CImg<T> get_load_camera(const unsigned int camera_index=0, const unsigned int skip_frames=0,
51198                                    const bool release_camera=true,
51199                                    const unsigned int capture_width=0, const unsigned int capture_height=0) {
51200       return CImg<T>().load_camera(camera_index,skip_frames,release_camera,capture_width,capture_height);
51201     }
51202 
51203     //! Load image using various non-native ways.
51204     /**
51205        \param filename Filename, as a C-string.
51206     **/
51207     CImg<T>& load_other(const char *const filename) {
51208       if (!filename)
51209         throw CImgArgumentException(_cimg_instance
51210                                     "load_other(): Specified filename is (null).",
51211                                     cimg_instance);
51212 
51213       const unsigned int omode = cimg::exception_mode();
51214       cimg::exception_mode(0);
51215       try { load_magick(filename); }
51216       catch (CImgException&) {
51217         try { load_imagemagick_external(filename); }
51218         catch (CImgException&) {
51219           try { load_graphicsmagick_external(filename); }
51220           catch (CImgException&) {
51221             try { load_cimg(filename); }
51222             catch (CImgException&) {
51223               try {
51224                 std::fclose(cimg::fopen(filename,"rb"));
51225               } catch (CImgException&) {
51226                 cimg::exception_mode(omode);
51227                 throw CImgIOException(_cimg_instance
51228                                       "load_other(): Failed to open file '%s'.",
51229                                       cimg_instance,
51230                                       filename);
51231               }
51232               cimg::exception_mode(omode);
51233               throw CImgIOException(_cimg_instance
51234                                     "load_other(): Failed to recognize format of file '%s'.",
51235                                     cimg_instance,
51236                                     filename);
51237             }
51238           }
51239         }
51240       }
51241       cimg::exception_mode(omode);
51242       return *this;
51243     }
51244 
51245     //! Load image using various non-native ways \newinstance.
51246     static CImg<T> get_load_other(const char *const filename) {
51247       return CImg<T>().load_other(filename);
51248     }
51249 
51250     //@}
51251     //---------------------------
51252     //
51253     //! \name Data Output
51254     //@{
51255     //---------------------------
51256 
51257     //! Display information about the image data.
51258     /**
51259        \param title Name for the considered image.
51260        \param display_stats Tells to compute and display image statistics.
51261     **/
51262     const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
51263 
51264       int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
51265       CImg<doubleT> st;
51266       if (!is_empty() && display_stats) {
51267         st = get_stats();
51268         xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
51269         xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
51270       }
51271 
51272       const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1,
51273         mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1;
51274 
51275       CImg<charT> _title(64);
51276       if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type());
51277 
51278       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
51279                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
51280                    cimg::t_bold,cimg::t_normal,(void*)this,
51281                    cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
51282                    (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))),
51283                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
51284                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
51285       if (_data)
51286         std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared");
51287       else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
51288 
51289       if (!is_empty()) cimg_foroff(*this,off) {
51290         std::fprintf(cimg::output(),"%g",(double)_data[off]);
51291         if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
51292         if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); }
51293       }
51294       if (!is_empty() && display_stats)
51295 	std::fprintf(cimg::output(),
51296                      " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), "
51297                      "%scoords_max%s = (%u,%u,%u,%u).\n",
51298 		     cimg::t_bold,cimg::t_normal,st[0],
51299                      cimg::t_bold,cimg::t_normal,st[1],
51300                      cimg::t_bold,cimg::t_normal,st[2],
51301                      cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
51302                      cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
51303                      cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
51304       else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
51305       std::fflush(cimg::output());
51306       return *this;
51307     }
51308 
51309     //! Display image into a CImgDisplay window.
51310     /**
51311        \param disp Display window.
51312     **/
51313     const CImg<T>& display(CImgDisplay& disp) const {
51314       disp.display(*this);
51315       return *this;
51316     }
51317 
51318     //! Display image into a CImgDisplay window, in an interactive way.
51319     /**
51320         \param disp Display window.
51321         \param display_info Tells if image information are displayed on the standard output.
51322         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
51323         \param exit_on_anykey Exit function when any key is pressed.
51324     **/
51325     const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0,
51326                            const bool exit_on_anykey=false) const {
51327       return _display(disp,0,display_info,XYZ,exit_on_anykey,false);
51328     }
51329 
51330     //! Display image into an interactive window.
51331     /**
51332         \param title Window title
51333         \param display_info Tells if image information are displayed on the standard output.
51334         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
51335         \param exit_on_anykey Exit function when any key is pressed.
51336     **/
51337     const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0,
51338                            const bool exit_on_anykey=false) const {
51339       CImgDisplay disp;
51340       return _display(disp,title,display_info,XYZ,exit_on_anykey,false);
51341     }
51342 
51343     const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
51344                             unsigned int *const XYZ, const bool exit_on_anykey,
51345                             const bool exit_on_simpleclick) const {
51346       unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0;
51347       int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1,
51348         old_mouse_x = -1, old_mouse_y = -1;
51349 
51350       if (!disp) {
51351         disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
51352         if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
51353         else disp.set_title("%s",title);
51354       } else if (title) disp.set_title("%s",title);
51355       disp.show().flush();
51356 
51357       const CImg<char> dtitle = CImg<char>::string(disp.title());
51358       if (display_info) print(dtitle);
51359 
51360       CImg<T> zoom;
51361       for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
51362         if (reset_view) {
51363           if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
51364           else {
51365             _XYZ[0] = (unsigned int)(x0 + x1)/2;
51366             _XYZ[1] = (unsigned int)(y0 + y1)/2;
51367             _XYZ[2] = (unsigned int)(z0 + z1)/2;
51368           }
51369           x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1;
51370           oldw = disp._width; oldh = disp._height;
51371           reset_view = false;
51372         }
51373         if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) {
51374           if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign();
51375         } else zoom = get_crop(x0,y0,z0,x1,y1,z1);
51376 
51377         const CImg<T>& visu = zoom?zoom:*this;
51378         const unsigned int
51379           dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0,
51380           tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U);
51381         if (!is_empty() && !disp.is_fullscreen() && resize_disp) {
51382           const unsigned int
51383             ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh,
51384             dM = std::max(ttw,tth), diM = (unsigned int)std::max(disp.width(),disp.height()),
51385             imgw = std::max(16U,ttw*diM/dM), imgh = std::max(16U,tth*diM/dM);
51386           disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
51387           resize_disp = false;
51388         }
51389         oldw = tw; oldh = th;
51390 
51391         bool
51392           go_up = false, go_down = false, go_left = false, go_right = false,
51393           go_inc = false, go_dec = false, go_in = false, go_out = false,
51394           go_in_center = false;
51395 
51396         disp.set_title("%s",dtitle._data);
51397         if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0);
51398         if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0);
51399         if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0);
51400 
51401         disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y;
51402         CImg<intT> selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true);
51403         old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y;
51404         is_first_select = false;
51405 
51406         if (disp.wheel()) {
51407           if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) &&
51408               (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) {
51409             go_left = !(go_right = disp.wheel()>0);
51410           } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) {
51411             go_down = !(go_up = disp.wheel()>0);
51412           } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51413             go_out = !(go_in = disp.wheel()>0); go_in_center = false;
51414           }
51415           disp.set_wheel();
51416         }
51417 
51418         const int
51419           sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
51420           sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
51421         if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
51422           x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1;
51423           x0+=sx0; y0+=sy0; z0+=sz0;
51424           if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) {
51425             if (exit_on_simpleclick && (!zoom || is_empty())) break; else reset_view = true;
51426           }
51427           resize_disp = true;
51428         } else switch (key = disp.key()) {
51429 #if cimg_OS!=2
51430           case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
51431 #endif
51432           case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break;
51433           case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) {
51434               // Special mode: play stack of frames
51435               const unsigned int
51436                 w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)),
51437                 h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0));
51438               float frame_timing = 5;
51439               bool is_stopped = false;
51440               disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
51441               for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
51442                 if (disp.is_resized()) disp.resize(false);
51443                 if (!timer) {
51444                   visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
51445                   (++_XYZ[2])%=visu._depth;
51446                 }
51447                 if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
51448                 if (disp.wheel()) { frame_timing-=disp.wheel()/3.0f; disp.set_wheel(); }
51449                 switch (key = disp.key()) {
51450 #if cimg_OS!=2
51451                 case cimg::keyCTRLRIGHT :
51452 #endif
51453                 case cimg::keyCTRLLEFT : key = 0; break;
51454                 case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
51455                 case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
51456                 case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
51457                 case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
51458                 case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true;
51459                   (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break;
51460                 case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51461                     disp.set_fullscreen(false).
51462                       resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
51463                              CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
51464                     disp.set_key(key,false); key = 0;
51465                   } break;
51466                 case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51467                     disp.set_fullscreen(false).
51468                       resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
51469                   } break;
51470                 case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51471                     disp.set_fullscreen(false).
51472                       resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
51473                   } break;
51474                 case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51475                     disp.resize(disp.screen_width(),disp.screen_height(),false).
51476                       toggle_fullscreen().set_key(key,false); key = 0;
51477                   } break;
51478                 }
51479                 frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
51480                 disp.wait(20);
51481               }
51482               const unsigned int
51483                 w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
51484                 h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
51485               disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
51486               key = 0;
51487             } break;
51488           case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
51489           case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
51490           case cimg::keyPADSUB : go_out = true; key = 0; break;
51491           case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
51492           case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
51493           case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
51494           case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
51495           case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
51496           case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
51497           case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
51498           case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
51499           case cimg::keyPAGEUP : go_inc = true; key = 0; break;
51500           case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
51501           }
51502         if (go_in) {
51503           const int
51504             mx = go_in_center?disp.width()/2:disp.mouse_x(),
51505             my = go_in_center?disp.height()/2:disp.mouse_y(),
51506             mX = mx*(width() + (depth()>1?depth():0))/disp.width(),
51507             mY = my*(height() + (depth()>1?depth():0))/disp.height();
51508           int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2];
51509           if (mX<width() && mY<height())  {
51510             X = x0 + mX*(1 + x1 - x0)/width(); Y = y0 + mY*(1 + y1 - y0)/height();
51511           }
51512           if (mX<width() && mY>=height()) {
51513             X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth();
51514           }
51515           if (mX>=width() && mY<height()) {
51516             Y = y0 + mY*(1 + y1 - y0)/height(); Z = z0 + (mX - width())*(1 + z1 - z0)/depth();
51517           }
51518           if (x1 - x0>4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; }
51519           if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; }
51520           if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; }
51521         }
51522         if (go_out) {
51523           const int
51524             delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8,
51525             ndelta_x = delta_x?delta_x:(_width>1),
51526             ndelta_y = delta_y?delta_y:(_height>1),
51527             ndelta_z = delta_z?delta_z:(_depth>1);
51528           x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
51529           x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
51530           if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
51531           if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
51532           if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
51533           if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; }
51534           if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; }
51535           if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; }
51536           const float
51537             ratio = (float)(x1-x0)/(y1-y0),
51538             ratiow = (float)disp._width/disp._height,
51539             sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow));
51540           if (sub>0.01) resize_disp = true;
51541         }
51542         if (go_left) {
51543           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
51544           if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
51545           else { x1-=x0; x0 = 0; }
51546         }
51547         if (go_right) {
51548           const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
51549           if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
51550           else { x0+=(width() - 1 - x1); x1 = width() - 1; }
51551         }
51552         if (go_up) {
51553           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
51554           if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; }
51555           else { y1-=y0; y0 = 0; }
51556         }
51557         if (go_down) {
51558           const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
51559           if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
51560           else { y0+=(height() - 1 - y1); y1 = height() - 1; }
51561         }
51562         if (go_inc) {
51563           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
51564           if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; }
51565           else { z1-=z0; z0 = 0; }
51566         }
51567         if (go_dec) {
51568           const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
51569           if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
51570           else { z0+=(depth() - 1 - z1); z1 = depth() - 1; }
51571         }
51572         disp.wait(100);
51573         if (!exit_on_anykey && key && key!=cimg::keyESC &&
51574             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
51575           key = 0;
51576         }
51577       }
51578       disp.set_key(key);
51579       if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
51580       return *this;
51581     }
51582 
51583     //! Display object 3d in an interactive window.
51584     /**
51585        \param disp Display window.
51586        \param vertices Vertices data of the 3d object.
51587        \param primitives Primitives data of the 3d object.
51588        \param colors Colors data of the 3d object.
51589        \param opacities Opacities data of the 3d object.
51590        \param centering Tells if the 3d object must be centered for the display.
51591        \param render_static Rendering mode.
51592        \param render_motion Rendering mode, when the 3d object is moved.
51593        \param is_double_sided Tells if the object primitives are double-sided.
51594        \param focale Focale
51595        \param light_x X-coordinate of the light source.
51596        \param light_y Y-coordinate of the light source.
51597        \param light_z Z-coordinate of the light source.
51598        \param specular_lightness Amount of specular light.
51599        \param specular_shininess Shininess of the object material.
51600        \param display_axes Tells if the 3d axes are displayed.
51601        \param pose_matrix Pointer to 12 values, defining a 3d pose (as a 4x3 matrix).
51602        \param exit_on_anykey Exit function when any key is pressed.
51603     **/
51604     template<typename tp, typename tf, typename tc, typename to>
51605     const CImg<T>& display_object3d(CImgDisplay& disp,
51606                                     const CImg<tp>& vertices,
51607                                     const CImgList<tf>& primitives,
51608                                     const CImgList<tc>& colors,
51609                                     const to& opacities,
51610                                     const bool centering=true,
51611                                     const int render_static=4, const int render_motion=1,
51612                                     const bool is_double_sided=true, const float focale=700,
51613                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51614                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51615                                     const bool display_axes=true, float *const pose_matrix=0,
51616                                     const bool exit_on_anykey=false) const {
51617       return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
51618 			       render_motion,is_double_sided,focale,
51619                                light_x,light_y,light_z,specular_lightness,specular_shininess,
51620 			       display_axes,pose_matrix,exit_on_anykey);
51621     }
51622 
51623     //! Display object 3d in an interactive window \simplification.
51624     template<typename tp, typename tf, typename tc, typename to>
51625     const CImg<T>& display_object3d(const char *const title,
51626                                     const CImg<tp>& vertices,
51627                                     const CImgList<tf>& primitives,
51628                                     const CImgList<tc>& colors,
51629                                     const to& opacities,
51630                                     const bool centering=true,
51631                                     const int render_static=4, const int render_motion=1,
51632                                     const bool is_double_sided=true, const float focale=700,
51633                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51634                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51635                                     const bool display_axes=true, float *const pose_matrix=0,
51636                                     const bool exit_on_anykey=false) const {
51637       CImgDisplay disp;
51638       return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
51639 			       render_motion,is_double_sided,focale,
51640                                light_x,light_y,light_z,specular_lightness,specular_shininess,
51641 			       display_axes,pose_matrix,exit_on_anykey);
51642     }
51643 
51644     //! Display object 3d in an interactive window \simplification.
51645     template<typename tp, typename tf, typename tc>
51646     const CImg<T>& display_object3d(CImgDisplay &disp,
51647                                     const CImg<tp>& vertices,
51648                                     const CImgList<tf>& primitives,
51649                                     const CImgList<tc>& colors,
51650                                     const bool centering=true,
51651                                     const int render_static=4, const int render_motion=1,
51652                                     const bool is_double_sided=true, const float focale=700,
51653                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51654                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51655                                     const bool display_axes=true, float *const pose_matrix=0,
51656                                     const bool exit_on_anykey=false) const {
51657       return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
51658 			      render_static,render_motion,is_double_sided,focale,
51659                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51660 			      display_axes,pose_matrix,exit_on_anykey);
51661     }
51662 
51663     //! Display object 3d in an interactive window \simplification.
51664     template<typename tp, typename tf, typename tc>
51665     const CImg<T>& display_object3d(const char *const title,
51666 				    const CImg<tp>& vertices,
51667                                     const CImgList<tf>& primitives,
51668                                     const CImgList<tc>& colors,
51669                                     const bool centering=true,
51670                                     const int render_static=4, const int render_motion=1,
51671                                     const bool is_double_sided=true, const float focale=700,
51672                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51673                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51674                                     const bool display_axes=true, float *const pose_matrix=0,
51675                                     const bool exit_on_anykey=false) const {
51676       return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
51677                               render_static,render_motion,is_double_sided,focale,
51678                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51679                               display_axes,pose_matrix,exit_on_anykey);
51680     }
51681 
51682     //! Display object 3d in an interactive window \simplification.
51683     template<typename tp, typename tf>
51684     const CImg<T>& display_object3d(CImgDisplay &disp,
51685                                     const CImg<tp>& vertices,
51686                                     const CImgList<tf>& primitives,
51687                                     const bool centering=true,
51688                                     const int render_static=4, const int render_motion=1,
51689                                     const bool is_double_sided=true, const float focale=700,
51690                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51691                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51692                                     const bool display_axes=true, float *const pose_matrix=0,
51693                                     const bool exit_on_anykey=false) const {
51694       return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
51695                               render_static,render_motion,is_double_sided,focale,
51696                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51697                               display_axes,pose_matrix,exit_on_anykey);
51698     }
51699 
51700 
51701     //! Display object 3d in an interactive window \simplification.
51702     template<typename tp, typename tf>
51703     const CImg<T>& display_object3d(const char *const title,
51704 				    const CImg<tp>& vertices,
51705                                     const CImgList<tf>& primitives,
51706                                     const bool centering=true,
51707                                     const int render_static=4, const int render_motion=1,
51708                                     const bool is_double_sided=true, const float focale=700,
51709                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51710                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51711                                     const bool display_axes=true, float *const pose_matrix=0,
51712                                     const bool exit_on_anykey=false) const {
51713       return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
51714                               render_static,render_motion,is_double_sided,focale,
51715                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51716                               display_axes,pose_matrix,exit_on_anykey);
51717     }
51718 
51719     //! Display object 3d in an interactive window \simplification.
51720     template<typename tp>
51721     const CImg<T>& display_object3d(CImgDisplay &disp,
51722                                     const CImg<tp>& vertices,
51723                                     const bool centering=true,
51724                                     const int render_static=4, const int render_motion=1,
51725                                     const bool is_double_sided=true, const float focale=700,
51726                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51727                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51728                                     const bool display_axes=true, float *const pose_matrix=0,
51729                                     const bool exit_on_anykey=false) const {
51730       return display_object3d(disp,vertices,CImgList<uintT>(),centering,
51731                               render_static,render_motion,is_double_sided,focale,
51732                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51733                               display_axes,pose_matrix,exit_on_anykey);
51734     }
51735 
51736     //! Display object 3d in an interactive window \simplification.
51737     template<typename tp>
51738     const CImg<T>& display_object3d(const char *const title,
51739 				    const CImg<tp>& vertices,
51740                                     const bool centering=true,
51741                                     const int render_static=4, const int render_motion=1,
51742                                     const bool is_double_sided=true, const float focale=700,
51743                                     const float light_x=0, const float light_y=0, const float light_z=-5e8f,
51744                                     const float specular_lightness=0.2f, const float specular_shininess=0.1f,
51745                                     const bool display_axes=true, float *const pose_matrix=0,
51746                                     const bool exit_on_anykey=false) const {
51747       return display_object3d(title,vertices,CImgList<uintT>(),centering,
51748                               render_static,render_motion,is_double_sided,focale,
51749                               light_x,light_y,light_z,specular_lightness,specular_shininess,
51750                               display_axes,pose_matrix,exit_on_anykey);
51751     }
51752 
51753     template<typename tp, typename tf, typename tc, typename to>
51754     const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
51755 				     const CImg<tp>& vertices,
51756 				     const CImgList<tf>& primitives,
51757 				     const CImgList<tc>& colors,
51758                                      const to& opacities,
51759 				     const bool centering,
51760 				     const int render_static, const int render_motion,
51761 				     const bool is_double_sided, const float focale,
51762                                      const float light_x, const float light_y, const float light_z,
51763 				     const float specular_lightness, const float specular_shininess,
51764 				     const bool display_axes, float *const pose_matrix,
51765                                      const bool exit_on_anykey) const {
51766       typedef typename cimg::superset<tp,float>::type tpfloat;
51767 
51768       // Check input arguments
51769       if (is_empty()) {
51770 	if (disp) return CImg<T>(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0).
51771 		    _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
51772                                       render_static,render_motion,is_double_sided,focale,
51773                                       light_x,light_y,light_z,specular_lightness,specular_shininess,
51774                                       display_axes,pose_matrix,exit_on_anykey);
51775 	else return CImg<T>(1,2,1,1,64,128).resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
51776                                                                   CImgDisplay::screen_height()/2,1),
51777                                                    1,(colors && colors[0].size()==1)?1:3,3).
51778                _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
51779 				 render_static,render_motion,is_double_sided,focale,
51780                                  light_x,light_y,light_z,specular_lightness,specular_shininess,
51781 				 display_axes,pose_matrix,exit_on_anykey);
51782       } else { if (disp) disp.resize(*this,false); }
51783       CImg<charT> error_message(1024);
51784       if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
51785         throw CImgArgumentException(_cimg_instance
51786                                     "display_object3d(): Invalid specified 3d object (%u,%u) (%s).",
51787                                     cimg_instance,vertices._width,primitives._width,error_message.data());
51788       if (vertices._width && !primitives) {
51789         CImgList<tf> nprimitives(vertices._width,1,1,1,1);
51790         cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l;
51791         return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
51792 				 render_static,render_motion,is_double_sided,focale,
51793                                  light_x,light_y,light_z,specular_lightness,specular_shininess,
51794 				 display_axes,pose_matrix,exit_on_anykey);
51795       }
51796       if (!disp) {
51797 	disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
51798         if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",
51799                                    pixel_type(),vertices._width,primitives._width);
51800       } else if (title) disp.set_title("%s",title);
51801 
51802       // Init 3d objects and compute object statistics
51803       CImg<floatT>
51804         pose,
51805         rotated_vertices(vertices._width,3),
51806         bbox_vertices, rotated_bbox_vertices,
51807         axes_vertices, rotated_axes_vertices,
51808         bbox_opacities, axes_opacities;
51809       CImgList<uintT> bbox_primitives, axes_primitives;
51810       CImgList<tf> reverse_primitives;
51811       CImgList<T> bbox_colors, bbox_colors2, axes_colors;
51812       unsigned int ns_width = 0, ns_height = 0;
51813       int _is_double_sided = (int)is_double_sided;
51814       bool ndisplay_axes = display_axes;
51815       const CImg<T>
51816         background_color(1,1,1,_spectrum,0),
51817         foreground_color(1,1,1,_spectrum,255);
51818       float
51819         Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
51820         xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
51821         ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
51822         zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
51823       const float delta = cimg::max(xM - xm,yM - ym,zM - zm);
51824 
51825       rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
51826                                                    xm,xM,xM,xm,xm,xM,xM,xm,
51827                                                    ym,ym,yM,yM,ym,ym,yM,yM,
51828                                                    zm,zm,zm,zm,zM,zM,zM,zM);
51829       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);
51830       bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
51831       bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
51832       bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
51833 
51834       rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
51835                                                    0,20,0,0,22,-6,-6,
51836                                                    0,0,20,0,-6,22,-6,
51837                                                    0,0,0,20,0,0,22);
51838       axes_opacities.assign(3,1,1,1,1);
51839       axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
51840       axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
51841 
51842       // Begin user interaction loop
51843       CImg<T> visu0(*this,false), visu;
51844       CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
51845       bool init_pose = true, clicked = false, redraw = true;
51846       unsigned int key = 0;
51847       int
51848         x0 = 0, y0 = 0, x1 = 0, y1 = 0,
51849         nrender_static = render_static,
51850         nrender_motion = render_motion;
51851       disp.show().flush();
51852 
51853       while (!disp.is_closed() && !key) {
51854 
51855         // Init object pose
51856         if (init_pose) {
51857           const float
51858             ratio = delta>0?(2.0f*std::min(disp.width(),disp.height())/(3.0f*delta)):1,
51859             dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
51860           if (centering)
51861             CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
51862           else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
51863           if (pose_matrix) {
51864             CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
51865             pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
51866             pose0(3,3) = pose(3,3) = 1;
51867             (pose0*pose).get_crop(0,0,3,2).move_to(pose);
51868             Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
51869           } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
51870           init_pose = false;
51871           redraw = true;
51872         }
51873 
51874         // Rotate and draw 3d object
51875         if (redraw) {
51876           const float
51877             r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
51878             r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
51879             r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
51880           if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0))
51881             cimg_forX(vertices,l) {
51882               const float x = (float)vertices(l,0), y = (float)vertices(l,1), z = (float)vertices(l,2);
51883               rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30;
51884               rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31;
51885               rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32;
51886             }
51887           else cimg_forX(bbox_vertices,l) {
51888               const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
51889               rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
51890               rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
51891               rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
51892             }
51893 
51894           // Draw objects
51895           const bool render_with_zbuffer = !clicked && nrender_static>0;
51896           visu = visu0;
51897           if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
51898             visu.draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
51899                                rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
51900               draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
51901                             rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
51902           else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(),
51903                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
51904                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
51905                                    colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
51906                                    width()/2.0f + light_x,height()/2.0f + light_y,light_z + Zoff,
51907                                    specular_lightness,specular_shininess,sprite_scale);
51908           // Draw axes
51909           if (ndisplay_axes) {
51910             const float
51911               n = 1e-8f + cimg::hypot(r00,r01,r02),
51912               _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
51913               _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
51914               _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
51915               Xaxes = 25, Yaxes = visu._height - 38.0f;
51916             cimg_forX(axes_vertices,l) {
51917               const float
51918                 x = axes_vertices(l,0),
51919                 y = axes_vertices(l,1),
51920                 z = axes_vertices(l,2);
51921               rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
51922               rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
51923               rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
51924             }
51925             axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f;
51926             axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f;
51927             axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f;
51928             visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,
51929                                axes_colors,axes_opacities,1,false,focale).
51930               draw_text((int)(Xaxes + rotated_axes_vertices(4,0)),
51931                         (int)(Yaxes + rotated_axes_vertices(4,1)),
51932                         "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
51933               draw_text((int)(Xaxes + rotated_axes_vertices(5,0)),
51934                         (int)(Yaxes + rotated_axes_vertices(5,1)),
51935                         "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
51936               draw_text((int)(Xaxes + rotated_axes_vertices(6,0)),
51937                         (int)(Yaxes + rotated_axes_vertices(6,1)),
51938                         "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
51939           }
51940           visu.display(disp);
51941           if (!clicked || nrender_motion==nrender_static) redraw = false;
51942         }
51943 
51944         // Handle user interaction
51945         disp.wait();
51946         if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
51947           redraw = true;
51948           if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
51949           else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
51950           if (disp.button()&1) {
51951             const float
51952               R = 0.45f*std::min(disp.width(),disp.height()),
51953               R2 = R*R,
51954               u0 = (float)(x0 - disp.width()/2),
51955               v0 = (float)(y0 - disp.height()/2),
51956               u1 = (float)(x1 - disp.width()/2),
51957               v1 = (float)(y1 - disp.height()/2),
51958               n0 = cimg::hypot(u0,v0),
51959               n1 = cimg::hypot(u1,v1),
51960               nu0 = n0>R?(u0*R/n0):u0,
51961               nv0 = n0>R?(v0*R/n0):v0,
51962               nw0 = (float)std::sqrt(std::max(0.0f,R2 - nu0*nu0 - nv0*nv0)),
51963               nu1 = n1>R?(u1*R/n1):u1,
51964               nv1 = n1>R?(v1*R/n1):v1,
51965               nw1 = (float)std::sqrt(std::max(0.0f,R2 - nu1*nu1 - nv1*nv1)),
51966               u = nv0*nw1 - nw0*nv1,
51967               v = nw0*nu1 - nu0*nw1,
51968               w = nv0*nu1 - nu0*nv1,
51969               n = cimg::hypot(u,v,w),
51970               alpha = (float)std::asin(n/R2)*180/cimg::PI;
51971             (CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose);
51972             x0 = x1; y0 = y1;
51973           }
51974           if (disp.button()&2) {
51975             if (focale>0) Zoff-=(y0 - y1)*focale/400;
51976             else { const float s = std::exp((y0 - y1)/400.0f); pose*=s; sprite_scale*=s; }
51977             x0 = x1; y0 = y1;
51978           }
51979           if (disp.wheel()) {
51980             if (focale>0) Zoff-=disp.wheel()*focale/20;
51981             else { const float s = std::exp(disp.wheel()/20.0f); pose*=s; sprite_scale*=s; }
51982             disp.set_wheel();
51983           }
51984           if (disp.button()&4) { Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1; }
51985           if ((disp.button()&1) && (disp.button()&2)) {
51986             init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
51987             pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
51988           }
51989         } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
51990 
51991         CImg<charT> filename(32);
51992         switch (key = disp.key()) {
51993 #if cimg_OS!=2
51994         case cimg::keyCTRLRIGHT :
51995 #endif
51996         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
51997         case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
51998             disp.set_fullscreen(false).
51999               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
52000                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
52001               _is_resized = true;
52002             disp.set_key(key,false); key = 0;
52003           } break;
52004         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52005             disp.set_fullscreen(false).
52006               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
52007             disp.set_key(key,false); key = 0;
52008           } break;
52009         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52010             disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
52011             disp.set_key(key,false); key = 0;
52012           } break;
52013         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52014             if (!ns_width || !ns_height ||
52015                 ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
52016               ns_width = disp.screen_width()*3U/4;
52017               ns_height = disp.screen_height()*3U/4;
52018             }
52019             if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
52020             else {
52021               ns_width = disp._width; ns_height = disp._height;
52022               disp.resize(disp.screen_width(),disp.screen_height(),false);
52023             }
52024             disp.toggle_fullscreen()._is_resized = true;
52025             disp.set_key(key,false); key = 0;
52026           } break;
52027         case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52028             // Switch single/double-sided primitives.
52029             if (--_is_double_sided==-2) _is_double_sided = 1;
52030             if (_is_double_sided>=0) reverse_primitives.assign();
52031             else primitives.get_reverse_object3d().move_to(reverse_primitives);
52032             disp.set_key(key,false); key = 0; redraw = true;
52033           } break;
52034         case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
52035             if (zbuffer) zbuffer.assign();
52036             else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
52037             disp.set_key(key,false); key = 0; redraw = true;
52038           } break;
52039         case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3d axes.
52040             ndisplay_axes = !ndisplay_axes;
52041             disp.set_key(key,false); key = 0; redraw = true;
52042           } break;
52043         case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points.
52044             nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
52045             disp.set_key(key,false); key = 0; redraw = true;
52046           } break;
52047         case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines.
52048             nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
52049             disp.set_key(key,false); key = 0; redraw = true;
52050           } break;
52051         case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat.
52052             nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
52053             disp.set_key(key,false); key = 0; redraw = true;
52054           } break;
52055         case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded.
52056             nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
52057             disp.set_key(key,false); key = 0; redraw = true;
52058           } break;
52059         case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52060             // Set rendering mode to gouraud-shaded.
52061             nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
52062             disp.set_key(key,false); key = 0; redraw = true;
52063           } break;
52064         case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded.
52065             nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
52066             disp.set_key(key,false); key = 0; redraw = true;
52067           } break;
52068         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
52069             static unsigned int snap_number = 0;
52070             std::FILE *file;
52071             do {
52072               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
52073               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52074             } while (file);
52075             (+visu).draw_text(0,0," Saving snapshot... ",
52076                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52077             visu.save(filename);
52078             (+visu).draw_text(0,0," Snapshot '%s' saved. ",
52079                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52080             disp.set_key(key,false); key = 0;
52081           } break;
52082         case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
52083             static unsigned int snap_number = 0;
52084             std::FILE *file;
52085             do {
52086               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++);
52087               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52088             } while (file);
52089             (+visu).draw_text(0,0," Saving object... ",
52090                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52091             vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
52092             (+visu).draw_text(0,0," Object '%s' saved. ",
52093                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52094             disp.set_key(key,false); key = 0;
52095           } break;
52096         case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
52097             static unsigned int snap_number = 0;
52098             std::FILE *file;
52099             do {
52100 #ifdef cimg_use_zlib
52101               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
52102 #else
52103               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
52104 #endif
52105               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52106             } while (file);
52107             (+visu).draw_text(0,0," Saving object... ",
52108                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52109             vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).
52110               save(filename);
52111             (+visu).draw_text(0,0," Object '%s' saved. ",
52112                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52113             disp.set_key(key,false); key = 0;
52114           } break;
52115 #ifdef cimg_use_board
52116         case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
52117             static unsigned int snap_number = 0;
52118             std::FILE *file;
52119             do {
52120               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++);
52121               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52122             } while (file);
52123             (+visu).draw_text(0,0," Saving EPS snapshot... ",
52124                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52125             LibBoard::Board board;
52126             (+visu)._draw_object3d(&board,zbuffer.fill(0),
52127                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
52128                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
52129                                    colors,opacities,clicked?nrender_motion:nrender_static,
52130                                    _is_double_sided==1,focale,
52131                                    visu.width()/2.0f + light_x,visu.height()/2.0f + light_y,light_z + Zoff,
52132                                    specular_lightness,specular_shininess,
52133                                    sprite_scale);
52134             board.saveEPS(filename);
52135             (+visu).draw_text(0,0," Object '%s' saved. ",
52136                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52137             disp.set_key(key,false); key = 0;
52138           } break;
52139         case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
52140             static unsigned int snap_number = 0;
52141             std::FILE *file;
52142             do {
52143               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++);
52144               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
52145             } while (file);
52146             (+visu).draw_text(0,0," Saving SVG snapshot... ",
52147                               foreground_color._data,background_color._data,0.7f,13).display(disp);
52148             LibBoard::Board board;
52149             (+visu)._draw_object3d(&board,zbuffer.fill(0),
52150                                    Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
52151                                    rotated_vertices,reverse_primitives?reverse_primitives:primitives,
52152                                    colors,opacities,clicked?nrender_motion:nrender_static,
52153                                    _is_double_sided==1,focale,
52154                                    visu.width()/2.0f + light_x,visu.height()/2.0f + light_y,light_z + Zoff,
52155                                    specular_lightness,specular_shininess,
52156                                    sprite_scale);
52157             board.saveSVG(filename);
52158             (+visu).draw_text(0,0," Object '%s' saved. ",
52159                               foreground_color._data,background_color._data,0.7f,13,filename._data).display(disp);
52160             disp.set_key(key,false); key = 0;
52161           } break;
52162 #endif
52163         }
52164         if (disp.is_resized()) {
52165           disp.resize(false); visu0 = get_resize(disp,1);
52166           if (zbuffer) zbuffer.assign(disp.width(),disp.height());
52167           redraw = true;
52168         }
52169         if (!exit_on_anykey && key && key!=cimg::keyESC &&
52170             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
52171           key = 0;
52172         }
52173       }
52174       if (pose_matrix) {
52175         std::memcpy(pose_matrix,pose._data,12*sizeof(float));
52176         pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
52177       }
52178       disp.set_button().set_key(key);
52179       return *this;
52180     }
52181 
52182     //! Display 1d graph in an interactive window.
52183     /**
52184        \param disp Display window.
52185        \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>.
52186        \param vertex_type Vertex type.
52187        \param labelx Title for the horizontal axis, as a C-string.
52188        \param xmin Minimum value along the X-axis.
52189        \param xmax Maximum value along the X-axis.
52190        \param labely Title for the vertical axis, as a C-string.
52191        \param ymin Minimum value along the X-axis.
52192        \param ymax Maximum value along the X-axis.
52193        \param exit_on_anykey Exit function when any key is pressed.
52194     **/
52195     const CImg<T>& display_graph(CImgDisplay &disp,
52196                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
52197                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
52198                                  const char *const labely=0, const double ymin=0, const double ymax=0,
52199                                  const bool exit_on_anykey=false) const {
52200       return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
52201     }
52202 
52203     //! Display 1d graph in an interactive window \overloading.
52204     const CImg<T>& display_graph(const char *const title=0,
52205                                  const unsigned int plot_type=1, const unsigned int vertex_type=1,
52206                                  const char *const labelx=0, const double xmin=0, const double xmax=0,
52207                                  const char *const labely=0, const double ymin=0, const double ymax=0,
52208                                  const bool exit_on_anykey=false) const {
52209       CImgDisplay disp;
52210       return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
52211     }
52212 
52213     const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0,
52214                                   const unsigned int plot_type=1, const unsigned int vertex_type=1,
52215                                   const char *const labelx=0, const double xmin=0, const double xmax=0,
52216                                   const char *const labely=0, const double ymin=0, const double ymax=0,
52217                                   const bool exit_on_anykey=false) const {
52218       if (is_empty())
52219         throw CImgInstanceException(_cimg_instance
52220                                     "display_graph(): Empty instance.",
52221                                     cimg_instance);
52222       if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
52223                    set_title(title?"%s":"CImg<%s>",title?title:pixel_type());
52224       const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1);
52225       const unsigned int old_normalization = disp.normalization();
52226       disp.show().flush()._normalization = 0;
52227 
52228       double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
52229       if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
52230       int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
52231 
52232       for (bool reset_view = true; !key && !disp.is_closed(); ) {
52233         if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; }
52234         CImg<T> zoom(x1 - x0 + 1,1,1,spectrum());
52235         cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true);
52236         if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
52237         if (y0==y1) { --y0; ++y1; }
52238 
52239         const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
52240         					           labelx,
52241                                                            nxmin + x0*(nxmax - nxmin)/siz1,
52242                                                            nxmin + x1*(nxmax - nxmin)/siz1,
52243                                                            labely,y0,y1,true);
52244 	const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
52245         if (selection[0]>=0) {
52246           if (selection[2]<0) reset_view = true;
52247           else {
52248             x1 = x0 + selection[2]; x0+=selection[0];
52249             if (selection[1]>=0 && selection[3]>=0) {
52250               y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32);
52251               y1-=selection[1]*(y1 - y0)/(disp.height() - 32);
52252             }
52253           }
52254         } else {
52255           bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
52256           switch (key = (int)disp.key()) {
52257           case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break;
52258           case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
52259           case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
52260           case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key();
52261             break;
52262           case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key();
52263             break;
52264           case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
52265           case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
52266           case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
52267           case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
52268           case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
52269           case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
52270           }
52271           if (disp.wheel()) {
52272             if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0);
52273             else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
52274             else go_out = !(go_in = disp.wheel()>0);
52275             key = 0;
52276           }
52277 
52278           if (go_in) {
52279             const int
52280               xsiz = x1 - x0,
52281               mx = (mouse_x - 16)*xsiz/(disp.width() - 32),
52282               cx = x0 + cimg::cut(mx,0,xsiz);
52283             if (x1 - x0>4) {
52284               x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8;
52285               if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
52286                 const double
52287                   ysiz = y1 - y0,
52288                   my = (mouse_y - 16)*ysiz/(disp.height() - 32),
52289                   cy = y1 - cimg::cut(my,0.0,ysiz);
52290                 y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8;
52291               } else y0 = y1 = 0;
52292             }
52293           }
52294           if (go_out) {
52295             if (x0>0 || x1<(int)siz1) {
52296               const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1);
52297               const double ndelta_y = (y1 - y0)/8;
52298               x0-=ndelta_x; x1+=ndelta_x;
52299               y0-=ndelta_y; y1+=ndelta_y;
52300               if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
52301               if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
52302             }
52303           }
52304           if (go_left) {
52305             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
52306             if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
52307             else { x1-=x0; x0 = 0; }
52308             go_left = false;
52309           }
52310           if (go_right) {
52311             const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
52312             if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
52313             else { x0+=(siz1 - x1); x1 = (int)siz1; }
52314             go_right = false;
52315           }
52316           if (go_up) {
52317             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
52318             y0+=ndelta; y1+=ndelta;
52319             go_up = false;
52320           }
52321           if (go_down) {
52322             const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
52323             y0-=ndelta; y1-=ndelta;
52324             go_down = false;
52325           }
52326         }
52327         if (!exit_on_anykey && key && key!=(int)cimg::keyESC &&
52328             (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
52329           disp.set_key(key,false);
52330           key = 0;
52331         }
52332       }
52333       disp._normalization = old_normalization;
52334       return *this;
52335     }
52336 
52337     //! Save image as a file.
52338     /**
52339        \param filename Filename, as a C-string.
52340        \param number When positive, represents an index added to the filename. Otherwise, no number is added.
52341        \param digits Number of digits used for adding the number to the filename.
52342        \note
52343        - The used file format is defined by the file extension in the filename \p filename.
52344        - Parameter \p number can be used to add a 6-digit number to the filename before saving.
52345 
52346     **/
52347     const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
52348       if (!filename)
52349         throw CImgArgumentException(_cimg_instance
52350                                     "save(): Specified filename is (null).",
52351                                     cimg_instance);
52352       // Do not test for empty instances, since .cimg format is able to manage empty instances.
52353       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
52354       const char *const ext = cimg::split_filename(filename);
52355       CImg<charT> nfilename(1024);
52356       const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
52357         filename;
52358 
52359 #ifdef cimg_save_plugin
52360       cimg_save_plugin(fn);
52361 #endif
52362 #ifdef cimg_save_plugin1
52363       cimg_save_plugin1(fn);
52364 #endif
52365 #ifdef cimg_save_plugin2
52366       cimg_save_plugin2(fn);
52367 #endif
52368 #ifdef cimg_save_plugin3
52369       cimg_save_plugin3(fn);
52370 #endif
52371 #ifdef cimg_save_plugin4
52372       cimg_save_plugin4(fn);
52373 #endif
52374 #ifdef cimg_save_plugin5
52375       cimg_save_plugin5(fn);
52376 #endif
52377 #ifdef cimg_save_plugin6
52378       cimg_save_plugin6(fn);
52379 #endif
52380 #ifdef cimg_save_plugin7
52381       cimg_save_plugin7(fn);
52382 #endif
52383 #ifdef cimg_save_plugin8
52384       cimg_save_plugin8(fn);
52385 #endif
52386       // Ascii formats
52387       if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
52388       else if (!cimg::strcasecmp(ext,"dlm") ||
52389                !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
52390       else if (!cimg::strcasecmp(ext,"cpp") ||
52391                !cimg::strcasecmp(ext,"hpp") ||
52392                !cimg::strcasecmp(ext,"h") ||
52393                !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
52394 
52395       // 2d binary formats
52396       else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
52397       else if (!cimg::strcasecmp(ext,"jpg") ||
52398                !cimg::strcasecmp(ext,"jpeg") ||
52399                !cimg::strcasecmp(ext,"jpe") ||
52400                !cimg::strcasecmp(ext,"jfif") ||
52401                !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
52402       else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
52403       else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
52404       else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
52405       else if (!cimg::strcasecmp(ext,"pgm") ||
52406                !cimg::strcasecmp(ext,"ppm") ||
52407                !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
52408       else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
52409       else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
52410       else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
52411       else if (!cimg::strcasecmp(ext,"tif") ||
52412                !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
52413 
52414       // 3d binary formats
52415       else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
52416       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
52417       else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
52418       else if (!cimg::strcasecmp(ext,"hdr") ||
52419                !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
52420       else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
52421       else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
52422       else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
52423       else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
52424 
52425       // Archive files
52426       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
52427 
52428       // Image sequences
52429       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
52430       else if (!cimg::strcasecmp(ext,"avi") ||
52431                !cimg::strcasecmp(ext,"mov") ||
52432                !cimg::strcasecmp(ext,"asf") ||
52433                !cimg::strcasecmp(ext,"divx") ||
52434                !cimg::strcasecmp(ext,"flv") ||
52435                !cimg::strcasecmp(ext,"mpg") ||
52436                !cimg::strcasecmp(ext,"m1v") ||
52437                !cimg::strcasecmp(ext,"m2v") ||
52438                !cimg::strcasecmp(ext,"m4v") ||
52439                !cimg::strcasecmp(ext,"mjp") ||
52440                !cimg::strcasecmp(ext,"mp4") ||
52441                !cimg::strcasecmp(ext,"mkv") ||
52442                !cimg::strcasecmp(ext,"mpe") ||
52443                !cimg::strcasecmp(ext,"movie") ||
52444                !cimg::strcasecmp(ext,"ogm") ||
52445                !cimg::strcasecmp(ext,"ogg") ||
52446                !cimg::strcasecmp(ext,"ogv") ||
52447                !cimg::strcasecmp(ext,"qt") ||
52448                !cimg::strcasecmp(ext,"rm") ||
52449                !cimg::strcasecmp(ext,"vob") ||
52450                !cimg::strcasecmp(ext,"wmv") ||
52451                !cimg::strcasecmp(ext,"xvid") ||
52452                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
52453       return save_other(fn);
52454     }
52455 
52456     //! Save image as an ascii file.
52457     /**
52458       \param filename Filename, as a C-string.
52459     **/
52460     const CImg<T>& save_ascii(const char *const filename) const {
52461       return _save_ascii(0,filename);
52462     }
52463 
52464     //! Save image as an ascii file \overloading.
52465     const CImg<T>& save_ascii(std::FILE *const file) const {
52466       return _save_ascii(file,0);
52467     }
52468 
52469     const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
52470       if (!file && !filename)
52471         throw CImgArgumentException(_cimg_instance
52472                                     "save_ascii(): Specified filename is (null).",
52473                                     cimg_instance);
52474       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
52475       std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
52476       const T* ptrs = _data;
52477       cimg_forYZC(*this,y,z,c) {
52478         cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++));
52479         std::fputc('\n',nfile);
52480       }
52481       if (!file) cimg::fclose(nfile);
52482       return *this;
52483     }
52484 
52485     //! Save image as a .cpp source file.
52486     /**
52487       \param filename Filename, as a C-string.
52488     **/
52489     const CImg<T>& save_cpp(const char *const filename) const {
52490       return _save_cpp(0,filename);
52491     }
52492 
52493     //! Save image as a .cpp source file \overloading.
52494     const CImg<T>& save_cpp(std::FILE *const file) const {
52495       return _save_cpp(file,0);
52496     }
52497 
52498     const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
52499       if (!file && !filename)
52500         throw CImgArgumentException(_cimg_instance
52501                                     "save_cpp(): Specified filename is (null).",
52502                                     cimg_instance);
52503       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
52504       CImg<charT> varname(1024); *varname = 0;
52505       if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data);
52506       if (!*varname) cimg_snprintf(varname,varname._width,"unnamed");
52507       std::fprintf(nfile,
52508                    "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
52509                    "%s data_%s[] = { %s\n  ",
52510                    varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data,
52511                    is_empty()?"};":"");
52512       if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) {
52513         std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
52514         if (off==siz) std::fprintf(nfile," };\n");
52515         else if (!((off + 1)%16)) std::fprintf(nfile,",\n  ");
52516         else std::fprintf(nfile,", ");
52517       }
52518       if (!file) cimg::fclose(nfile);
52519       return *this;
52520     }
52521 
52522     //! Save image as a DLM file.
52523     /**
52524        \param filename Filename, as a C-string.
52525     **/
52526     const CImg<T>& save_dlm(const char *const filename) const {
52527       return _save_dlm(0,filename);
52528     }
52529 
52530     //! Save image as a DLM file \overloading.
52531     const CImg<T>& save_dlm(std::FILE *const file) const {
52532       return _save_dlm(file,0);
52533     }
52534 
52535     const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
52536       if (!file && !filename)
52537         throw CImgArgumentException(_cimg_instance
52538                                     "save_dlm(): Specified filename is (null).",
52539                                     cimg_instance);
52540       if (is_empty()) { cimg::fempty(file,filename); return *this; }
52541       if (_depth>1)
52542         cimg::warn(_cimg_instance
52543                    "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
52544                    cimg_instance,
52545                    filename?filename:"(FILE*)");
52546       if (_spectrum>1)
52547         cimg::warn(_cimg_instance
52548                    "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
52549                    cimg_instance,
52550                    filename?filename:"(FILE*)");
52551 
52552       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
52553       const T* ptrs = _data;
52554       cimg_forYZC(*this,y,z,c) {
52555         cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":",");
52556         std::fputc('\n',nfile);
52557       }
52558       if (!file) cimg::fclose(nfile);
52559       return *this;
52560     }
52561 
52562     //! Save image as a BMP file.
52563     /**
52564       \param filename Filename, as a C-string.
52565     **/
52566     const CImg<T>& save_bmp(const char *const filename) const {
52567       return _save_bmp(0,filename);
52568     }
52569 
52570     //! Save image as a BMP file \overloading.
52571     const CImg<T>& save_bmp(std::FILE *const file) const {
52572       return _save_bmp(file,0);
52573     }
52574 
52575     const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
52576       if (!file && !filename)
52577         throw CImgArgumentException(_cimg_instance
52578                                     "save_bmp(): Specified filename is (null).",
52579                                     cimg_instance);
52580       if (is_empty()) { cimg::fempty(file,filename); return *this; }
52581       if (_depth>1)
52582         cimg::warn(_cimg_instance
52583                    "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
52584                    cimg_instance,
52585                    filename?filename:"(FILE*)");
52586       if (_spectrum>3)
52587         cimg::warn(_cimg_instance
52588                    "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
52589                    cimg_instance,
52590                    filename?filename:"(FILE*)");
52591 
52592       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
52593       CImg<ucharT> header(54,1,1,1,0);
52594       unsigned char align_buf[4] = { 0 };
52595       const unsigned int
52596         align = (4 - (3*_width)%4)%4,
52597         buf_size = (3*_width + align)*height(),
52598         file_size = 54 + buf_size;
52599       header[0] = 'B'; header[1] = 'M';
52600       header[0x02] = file_size&0xFF;
52601       header[0x03] = (file_size>>8)&0xFF;
52602       header[0x04] = (file_size>>16)&0xFF;
52603       header[0x05] = (file_size>>24)&0xFF;
52604       header[0x0A] = 0x36;
52605       header[0x0E] = 0x28;
52606       header[0x12] = _width&0xFF;
52607       header[0x13] = (_width>>8)&0xFF;
52608       header[0x14] = (_width>>16)&0xFF;
52609       header[0x15] = (_width>>24)&0xFF;
52610       header[0x16] = _height&0xFF;
52611       header[0x17] = (_height>>8)&0xFF;
52612       header[0x18] = (_height>>16)&0xFF;
52613       header[0x19] = (_height>>24)&0xFF;
52614       header[0x1A] = 1;
52615       header[0x1B] = 0;
52616       header[0x1C] = 24;
52617       header[0x1D] = 0;
52618       header[0x22] = buf_size&0xFF;
52619       header[0x23] = (buf_size>>8)&0xFF;
52620       header[0x24] = (buf_size>>16)&0xFF;
52621       header[0x25] = (buf_size>>24)&0xFF;
52622       header[0x27] = 0x1;
52623       header[0x2B] = 0x1;
52624       cimg::fwrite(header._data,54,nfile);
52625 
52626       const T
52627         *ptr_r = data(0,_height - 1,0,0),
52628         *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0,
52629         *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0;
52630 
52631       switch (_spectrum) {
52632       case 1 : {
52633         cimg_forY(*this,y) {
52634           cimg_forX(*this,x) {
52635             const unsigned char val = (unsigned char)*(ptr_r++);
52636             std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
52637           }
52638           cimg::fwrite(align_buf,align,nfile);
52639           ptr_r-=2*_width;
52640         }
52641       } break;
52642       case 2 : {
52643         cimg_forY(*this,y) {
52644           cimg_forX(*this,x) {
52645             std::fputc(0,nfile);
52646             std::fputc((unsigned char)(*(ptr_g++)),nfile);
52647             std::fputc((unsigned char)(*(ptr_r++)),nfile);
52648           }
52649           cimg::fwrite(align_buf,align,nfile);
52650           ptr_r-=2*_width; ptr_g-=2*_width;
52651         }
52652       } break;
52653       default : {
52654         cimg_forY(*this,y) {
52655           cimg_forX(*this,x) {
52656             std::fputc((unsigned char)(*(ptr_b++)),nfile);
52657             std::fputc((unsigned char)(*(ptr_g++)),nfile);
52658             std::fputc((unsigned char)(*(ptr_r++)),nfile);
52659           }
52660           cimg::fwrite(align_buf,align,nfile);
52661           ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
52662         }
52663       }
52664       }
52665       if (!file) cimg::fclose(nfile);
52666       return *this;
52667     }
52668 
52669     //! Save image as a JPEG file.
52670     /**
52671       \param filename Filename, as a C-string.
52672       \param quality Image quality (in %)
52673     **/
52674     const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
52675       return _save_jpeg(0,filename,quality);
52676     }
52677 
52678     //! Save image as a JPEG file \overloading.
52679     const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
52680       return _save_jpeg(file,0,quality);
52681     }
52682 
52683     const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
52684       if (!file && !filename)
52685         throw CImgArgumentException(_cimg_instance
52686                                     "save_jpeg(): Specified filename is (null).",
52687                                     cimg_instance);
52688       if (is_empty()) { cimg::fempty(file,filename); return *this; }
52689       if (_depth>1)
52690         cimg::warn(_cimg_instance
52691                    "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
52692                    cimg_instance,
52693                    filename?filename:"(FILE*)");
52694 
52695 #ifndef cimg_use_jpeg
52696       if (!file) return save_other(filename,quality);
52697       else throw CImgIOException(_cimg_instance
52698                                  "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
52699                                  cimg_instance);
52700 #else
52701       unsigned int dimbuf = 0;
52702       J_COLOR_SPACE colortype = JCS_RGB;
52703 
52704       switch (_spectrum) {
52705       case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
52706       case 2 : dimbuf = 3; colortype = JCS_RGB; break;
52707       case 3 : dimbuf = 3; colortype = JCS_RGB; break;
52708       default : dimbuf = 4; colortype = JCS_CMYK; break;
52709       }
52710 
52711       // Call libjpeg functions
52712       struct jpeg_compress_struct cinfo;
52713       struct jpeg_error_mgr jerr;
52714       cinfo.err = jpeg_std_error(&jerr);
52715       jpeg_create_compress(&cinfo);
52716       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
52717       jpeg_stdio_dest(&cinfo,nfile);
52718       cinfo.image_width = _width;
52719       cinfo.image_height = _height;
52720       cinfo.input_components = dimbuf;
52721       cinfo.in_color_space = colortype;
52722       jpeg_set_defaults(&cinfo);
52723       jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
52724       jpeg_start_compress(&cinfo,TRUE);
52725 
52726       JSAMPROW row_pointer[1];
52727       CImg<ucharT> buffer(_width*dimbuf);
52728 
52729       while (cinfo.next_scanline<cinfo.image_height) {
52730         unsigned char *ptrd = buffer._data;
52731 
52732         // Fill pixel buffer
52733         switch (_spectrum) {
52734         case 1 : { // Greyscale images
52735           const T *ptr_g = data(0, cinfo.next_scanline);
52736           for (unsigned int b = 0; b<cinfo.image_width; b++)
52737             *(ptrd++) = (unsigned char)*(ptr_g++);
52738         } break;
52739         case 2 : { // RG images
52740           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
52741             *ptr_g = data(0,cinfo.next_scanline,0,1);
52742           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
52743             *(ptrd++) = (unsigned char)*(ptr_r++);
52744             *(ptrd++) = (unsigned char)*(ptr_g++);
52745             *(ptrd++) = 0;
52746           }
52747         } break;
52748         case 3 : { // RGB images
52749           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
52750             *ptr_g = data(0,cinfo.next_scanline,0,1),
52751             *ptr_b = data(0,cinfo.next_scanline,0,2);
52752           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
52753             *(ptrd++) = (unsigned char)*(ptr_r++);
52754             *(ptrd++) = (unsigned char)*(ptr_g++);
52755             *(ptrd++) = (unsigned char)*(ptr_b++);
52756           }
52757         } break;
52758         default : { // CMYK images
52759           const T *ptr_r = data(0,cinfo.next_scanline,0,0),
52760             *ptr_g = data(0,cinfo.next_scanline,0,1),
52761             *ptr_b = data(0,cinfo.next_scanline,0,2),
52762             *ptr_a = data(0,cinfo.next_scanline,0,3);
52763           for (unsigned int b = 0; b<cinfo.image_width; ++b) {
52764             *(ptrd++) = (unsigned char)*(ptr_r++);
52765             *(ptrd++) = (unsigned char)*(ptr_g++);
52766             *(ptrd++) = (unsigned char)*(ptr_b++);
52767             *(ptrd++) = (unsigned char)*(ptr_a++);
52768           }
52769         }
52770         }
52771         *row_pointer = buffer._data;
52772         jpeg_write_scanlines(&cinfo,row_pointer,1);
52773       }
52774       jpeg_finish_compress(&cinfo);
52775       if (!file) cimg::fclose(nfile);
52776       jpeg_destroy_compress(&cinfo);
52777       return *this;
52778 #endif
52779     }
52780 
52781     //! Save image, using built-in ImageMagick++ library.
52782     /**
52783       \param filename Filename, as a C-string.
52784       \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible.
52785     **/
52786     const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
52787       if (!filename)
52788         throw CImgArgumentException(_cimg_instance
52789                                     "save_magick(): Specified filename is (null).",
52790                                     cimg_instance);
52791       if (is_empty()) { cimg::fempty(0,filename); return *this; }
52792 
52793 #ifdef cimg_use_magick
52794       double stmin, stmax = (double)max_min(stmin);
52795       if (_depth>1)
52796         cimg::warn(_cimg_instance
52797                    "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
52798                    cimg_instance,
52799                    filename);
52800 
52801       if (_spectrum>3)
52802         cimg::warn(_cimg_instance
52803                    "save_magick(): Instance is multispectral, only the three first channels will be "
52804                    "saved in file '%s'.",
52805                    cimg_instance,
52806                    filename);
52807 
52808       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
52809         cimg::warn(_cimg_instance
52810                    "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
52811                    cimg_instance,
52812                    filename,stmin,stmax);
52813 
52814       Magick::Image image(Magick::Geometry(_width,_height),"black");
52815       image.type(Magick::TrueColorType);
52816       image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
52817       const T
52818         *ptr_r = data(0,0,0,0),
52819         *ptr_g = _spectrum>1?data(0,0,0,1):0,
52820         *ptr_b = _spectrum>2?data(0,0,0,2):0;
52821       Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
52822       switch (_spectrum) {
52823       case 1 : // Scalar images
52824         for (ulongT off = (ulongT)_width*_height; off; --off) {
52825           pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
52826           ++pixels;
52827         }
52828         break;
52829       case 2 : // RG images
52830         for (ulongT off = (ulongT)_width*_height; off; --off) {
52831           pixels->red = (Magick::Quantum)*(ptr_r++);
52832           pixels->green = (Magick::Quantum)*(ptr_g++);
52833           pixels->blue = 0; ++pixels;
52834         }
52835         break;
52836       default : // RGB images
52837         for (ulongT off = (ulongT)_width*_height; off; --off) {
52838           pixels->red = (Magick::Quantum)*(ptr_r++);
52839           pixels->green = (Magick::Quantum)*(ptr_g++);
52840           pixels->blue = (Magick::Quantum)*(ptr_b++);
52841           ++pixels;
52842         }
52843       }
52844       image.syncPixels();
52845       image.write(filename);
52846       return *this;
52847 #else
52848       cimg::unused(bytes_per_pixel);
52849       throw CImgIOException(_cimg_instance
52850                             "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
52851                             cimg_instance,
52852                             filename);
52853 #endif
52854     }
52855 
52856     //! Save image as a PNG file.
52857     /**
52858        \param filename Filename, as a C-string.
52859        \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
52860     **/
52861     const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
52862       return _save_png(0,filename,bytes_per_pixel);
52863     }
52864 
52865     //! Save image as a PNG file \overloading.
52866     const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
52867       return _save_png(file,0,bytes_per_pixel);
52868     }
52869 
52870     const CImg<T>& _save_png(std::FILE *const file, const char *const filename,
52871                              const unsigned int bytes_per_pixel=0) const {
52872       if (!file && !filename)
52873         throw CImgArgumentException(_cimg_instance
52874                                     "save_png(): Specified filename is (null).",
52875                                     cimg_instance);
52876       if (is_empty()) { cimg::fempty(file,filename); return *this; }
52877 
52878 #ifndef cimg_use_png
52879       cimg::unused(bytes_per_pixel);
52880       if (!file) return save_other(filename);
52881       else throw CImgIOException(_cimg_instance
52882                                  "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
52883                                  cimg_instance);
52884 #else
52885 
52886 #if defined __GNUC__
52887       const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning.
52888       std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
52889       volatile double stmin, stmax = (double)max_min(stmin);
52890 #else
52891       const char *nfilename = filename;
52892       std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb");
52893       double stmin, stmax = (double)max_min(stmin);
52894 #endif
52895 
52896       if (_depth>1)
52897         cimg::warn(_cimg_instance
52898                    "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
52899                    cimg_instance,
52900                    filename);
52901 
52902       if (_spectrum>4)
52903         cimg::warn(_cimg_instance
52904                    "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
52905                    cimg_instance,
52906                    filename);
52907 
52908       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
52909         cimg::warn(_cimg_instance
52910                    "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
52911                    cimg_instance,
52912                    filename,stmin,stmax);
52913 
52914       // Setup PNG structures for write
52915       png_voidp user_error_ptr = 0;
52916       png_error_ptr user_error_fn = 0, user_warning_fn = 0;
52917       png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn,
52918                                                     user_warning_fn);
52919       if (!png_ptr){
52920         if (!file) cimg::fclose(nfile);
52921         throw CImgIOException(_cimg_instance
52922                               "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
52923                               cimg_instance,
52924                               nfilename?nfilename:"(FILE*)");
52925       }
52926       png_infop info_ptr = png_create_info_struct(png_ptr);
52927       if (!info_ptr) {
52928         png_destroy_write_struct(&png_ptr,(png_infopp)0);
52929         if (!file) cimg::fclose(nfile);
52930         throw CImgIOException(_cimg_instance
52931                               "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
52932                               cimg_instance,
52933                               nfilename?nfilename:"(FILE*)");
52934       }
52935       if (setjmp(png_jmpbuf(png_ptr))) {
52936         png_destroy_write_struct(&png_ptr, &info_ptr);
52937         if (!file) cimg::fclose(nfile);
52938         throw CImgIOException(_cimg_instance
52939                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
52940                               cimg_instance,
52941                               nfilename?nfilename:"(FILE*)");
52942       }
52943       png_init_io(png_ptr, nfile);
52944 
52945       const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
52946 
52947       int color_type;
52948       switch (spectrum()) {
52949       case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
52950       case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
52951       case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
52952       default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
52953       }
52954       const int interlace_type = PNG_INTERLACE_NONE;
52955       const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
52956       const int filter_method = PNG_FILTER_TYPE_DEFAULT;
52957       png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
52958       png_write_info(png_ptr,info_ptr);
52959       const int byte_depth = bit_depth>>3;
52960       const int numChan = spectrum()>4?4:spectrum();
52961       const int pixel_bit_depth_flag = numChan * (bit_depth - 1);
52962 
52963       // Allocate Memory for Image Save and Fill pixel data
52964       png_bytep *const imgData = new png_byte*[_height];
52965       for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
52966       const T *pC0 = data(0,0,0,0);
52967       switch (pixel_bit_depth_flag) {
52968       case 7 :  { // Gray 8-bit
52969         cimg_forY(*this,y) {
52970           unsigned char *ptrd = imgData[y];
52971           cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
52972         }
52973       } break;
52974       case 14 : { // Gray w/ Alpha 8-bit
52975         const T *pC1 = data(0,0,0,1);
52976         cimg_forY(*this,y) {
52977           unsigned char *ptrd = imgData[y];
52978           cimg_forX(*this,x) {
52979             *(ptrd++) = (unsigned char)*(pC0++);
52980             *(ptrd++) = (unsigned char)*(pC1++);
52981           }
52982         }
52983       } break;
52984       case 21 :  { // RGB 8-bit
52985         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
52986         cimg_forY(*this,y) {
52987           unsigned char *ptrd = imgData[y];
52988           cimg_forX(*this,x) {
52989             *(ptrd++) = (unsigned char)*(pC0++);
52990             *(ptrd++) = (unsigned char)*(pC1++);
52991             *(ptrd++) = (unsigned char)*(pC2++);
52992           }
52993         }
52994       } break;
52995       case 28 : { // RGB x/ Alpha 8-bit
52996         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
52997         cimg_forY(*this,y){
52998           unsigned char *ptrd = imgData[y];
52999           cimg_forX(*this,x){
53000             *(ptrd++) = (unsigned char)*(pC0++);
53001             *(ptrd++) = (unsigned char)*(pC1++);
53002             *(ptrd++) = (unsigned char)*(pC2++);
53003             *(ptrd++) = (unsigned char)*(pC3++);
53004           }
53005         }
53006       } break;
53007       case 15 : { // Gray 16-bit
53008         cimg_forY(*this,y){
53009           unsigned short *ptrd = (unsigned short*)(imgData[y]);
53010           cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
53011           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
53012         }
53013       } break;
53014       case 30 : { // Gray w/ Alpha 16-bit
53015         const T *pC1 = data(0,0,0,1);
53016         cimg_forY(*this,y){
53017           unsigned short *ptrd = (unsigned short*)(imgData[y]);
53018           cimg_forX(*this,x) {
53019             *(ptrd++) = (unsigned short)*(pC0++);
53020             *(ptrd++) = (unsigned short)*(pC1++);
53021           }
53022           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
53023         }
53024       } break;
53025       case 45 : { // RGB 16-bit
53026         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
53027         cimg_forY(*this,y) {
53028           unsigned short *ptrd = (unsigned short*)(imgData[y]);
53029           cimg_forX(*this,x) {
53030             *(ptrd++) = (unsigned short)*(pC0++);
53031             *(ptrd++) = (unsigned short)*(pC1++);
53032             *(ptrd++) = (unsigned short)*(pC2++);
53033           }
53034           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
53035         }
53036       } break;
53037       case 60 : { // RGB w/ Alpha 16-bit
53038         const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
53039         cimg_forY(*this,y) {
53040           unsigned short *ptrd = (unsigned short*)(imgData[y]);
53041           cimg_forX(*this,x) {
53042             *(ptrd++) = (unsigned short)*(pC0++);
53043             *(ptrd++) = (unsigned short)*(pC1++);
53044             *(ptrd++) = (unsigned short)*(pC2++);
53045             *(ptrd++) = (unsigned short)*(pC3++);
53046           }
53047           if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
53048         }
53049       } break;
53050       default :
53051         if (!file) cimg::fclose(nfile);
53052         throw CImgIOException(_cimg_instance
53053                               "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
53054                               cimg_instance,
53055                               nfilename?nfilename:"(FILE*)");
53056       }
53057       png_write_image(png_ptr,imgData);
53058       png_write_end(png_ptr,info_ptr);
53059       png_destroy_write_struct(&png_ptr, &info_ptr);
53060 
53061       // Deallocate Image Write Memory
53062       cimg_forY(*this,n) delete[] imgData[n];
53063       delete[] imgData;
53064 
53065       if (!file) cimg::fclose(nfile);
53066       return *this;
53067 #endif
53068     }
53069 
53070     //! Save image as a PNM file.
53071     /**
53072       \param filename Filename, as a C-string.
53073       \param bytes_per_pixel Force the number of bytes per pixels for the saving.
53074     **/
53075     const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
53076       return _save_pnm(0,filename,bytes_per_pixel);
53077     }
53078 
53079     //! Save image as a PNM file \overloading.
53080     const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
53081       return _save_pnm(file,0,bytes_per_pixel);
53082     }
53083 
53084     const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename,
53085                              const unsigned int bytes_per_pixel=0) const {
53086       if (!file && !filename)
53087         throw CImgArgumentException(_cimg_instance
53088                                     "save_pnm(): Specified filename is (null).",
53089                                     cimg_instance);
53090       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53091 
53092       double stmin, stmax = (double)max_min(stmin);
53093       if (_depth>1)
53094         cimg::warn(_cimg_instance
53095                    "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
53096                    cimg_instance,
53097                    filename?filename:"(FILE*)");
53098       if (_spectrum>3)
53099         cimg::warn(_cimg_instance
53100                    "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
53101                    cimg_instance,
53102                    filename?filename:"(FILE*)");
53103       if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
53104         cimg::warn(_cimg_instance
53105                    "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
53106                    cimg_instance,
53107                    stmin,stmax,filename?filename:"(FILE*)");
53108 
53109       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53110       const T
53111         *ptr_r = data(0,0,0,0),
53112         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
53113         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
53114       const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL)));
53115 
53116       std::fprintf(nfile,"P%c\n%u %u\n%u\n",
53117                    (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
53118 
53119       switch (_spectrum) {
53120       case 1 : { // Scalar image
53121         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
53122           CImg<ucharT> buf((unsigned int)buf_size);
53123           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53124             const ulongT N = std::min((ulongT)to_write,buf_size);
53125             unsigned char *ptrd = buf._data;
53126             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
53127             cimg::fwrite(buf._data,N,nfile);
53128             to_write-=N;
53129           }
53130         } else { // Binary PGM 16 bits
53131           CImg<ushortT> buf((unsigned int)buf_size);
53132           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53133             const ulongT N = std::min((ulongT)to_write,buf_size);
53134             unsigned short *ptrd = buf._data;
53135             for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
53136             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53137             cimg::fwrite(buf._data,N,nfile);
53138             to_write-=N;
53139           }
53140         }
53141       } break;
53142       case 2 : { // RG image
53143         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
53144           CImg<ucharT> buf((unsigned int)buf_size);
53145           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53146             const ulongT N = std::min((ulongT)to_write,buf_size/3);
53147             unsigned char *ptrd = buf._data;
53148             for (ulongT i = N; i>0; --i) {
53149               *(ptrd++) = (unsigned char)*(ptr_r++);
53150               *(ptrd++) = (unsigned char)*(ptr_g++);
53151               *(ptrd++) = 0;
53152             }
53153             cimg::fwrite(buf._data,3*N,nfile);
53154             to_write-=N;
53155           }
53156         } else {             // Binary PPM 16 bits
53157           CImg<ushortT> buf((unsigned int)buf_size);
53158           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53159             const ulongT N = std::min((ulongT)to_write,buf_size/3);
53160             unsigned short *ptrd = buf._data;
53161             for (ulongT i = N; i>0; --i) {
53162               *(ptrd++) = (unsigned short)*(ptr_r++);
53163               *(ptrd++) = (unsigned short)*(ptr_g++);
53164               *(ptrd++) = 0;
53165             }
53166             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53167             cimg::fwrite(buf._data,3*N,nfile);
53168             to_write-=N;
53169           }
53170         }
53171       } break;
53172       default : { // RGB image
53173         if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
53174           CImg<ucharT> buf((unsigned int)buf_size);
53175           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53176             const ulongT N = std::min((ulongT)to_write,buf_size/3);
53177             unsigned char *ptrd = buf._data;
53178             for (ulongT i = N; i>0; --i) {
53179               *(ptrd++) = (unsigned char)*(ptr_r++);
53180               *(ptrd++) = (unsigned char)*(ptr_g++);
53181               *(ptrd++) = (unsigned char)*(ptr_b++);
53182             }
53183             cimg::fwrite(buf._data,3*N,nfile);
53184             to_write-=N;
53185           }
53186         } else { // Binary PPM 16 bits
53187           CImg<ushortT> buf((unsigned int)buf_size);
53188           for (longT to_write = (longT)width()*height(); to_write>0; ) {
53189             const ulongT N = std::min((ulongT)to_write,buf_size/3);
53190             unsigned short *ptrd = buf._data;
53191             for (ulongT i = N; i>0; --i) {
53192               *(ptrd++) = (unsigned short)*(ptr_r++);
53193               *(ptrd++) = (unsigned short)*(ptr_g++);
53194               *(ptrd++) = (unsigned short)*(ptr_b++);
53195             }
53196             if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53197             cimg::fwrite(buf._data,3*N,nfile);
53198             to_write-=N;
53199           }
53200         }
53201       }
53202       }
53203       if (!file) cimg::fclose(nfile);
53204       return *this;
53205     }
53206 
53207     //! Save image as a PNK file.
53208     /**
53209       \param filename Filename, as a C-string.
53210     **/
53211     const CImg<T>& save_pnk(const char *const filename) const {
53212       return _save_pnk(0,filename);
53213     }
53214 
53215     //! Save image as a PNK file \overloading.
53216     const CImg<T>& save_pnk(std::FILE *const file) const {
53217       return _save_pnk(file,0);
53218     }
53219 
53220     const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
53221       if (!file && !filename)
53222         throw CImgArgumentException(_cimg_instance
53223                                     "save_pnk(): Specified filename is (null).",
53224                                     cimg_instance);
53225       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53226       if (_spectrum>1)
53227         cimg::warn(_cimg_instance
53228                    "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
53229                    cimg_instance,
53230                    filename?filename:"(FILE*)");
53231 
53232       const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth);
53233       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53234       const T *ptr = data(0,0,0,0);
53235 
53236       if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file.
53237         _save_pnm(file,filename,0);
53238       else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3d.
53239         std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
53240         CImg<ucharT> buf((unsigned int)buf_size);
53241         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
53242           const ulongT N = std::min((ulongT)to_write,buf_size);
53243           unsigned char *ptrd = buf._data;
53244           for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
53245           cimg::fwrite(buf._data,N,nfile);
53246           to_write-=N;
53247         }
53248       } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3d.
53249         if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
53250         else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
53251         CImg<intT> buf((unsigned int)buf_size);
53252         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
53253           const ulongT N = std::min((ulongT)to_write,buf_size);
53254           int *ptrd = buf._data;
53255           for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
53256           cimg::fwrite(buf._data,N,nfile);
53257           to_write-=N;
53258         }
53259       } else { // Save as P9: Binary float-valued 3d.
53260         if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
53261         else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
53262         CImg<floatT> buf((unsigned int)buf_size);
53263         for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
53264           const ulongT N = std::min((ulongT)to_write,buf_size);
53265           float *ptrd = buf._data;
53266           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
53267           cimg::fwrite(buf._data,N,nfile);
53268           to_write-=N;
53269         }
53270       }
53271 
53272       if (!file) cimg::fclose(nfile);
53273       return *this;
53274     }
53275 
53276     //! Save image as a PFM file.
53277     /**
53278       \param filename Filename, as a C-string.
53279     **/
53280     const CImg<T>& save_pfm(const char *const filename) const {
53281       get_mirror('y')._save_pfm(0,filename);
53282       return *this;
53283     }
53284 
53285     //! Save image as a PFM file \overloading.
53286     const CImg<T>& save_pfm(std::FILE *const file) const {
53287       get_mirror('y')._save_pfm(file,0);
53288       return *this;
53289     }
53290 
53291     const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
53292       if (!file && !filename)
53293         throw CImgArgumentException(_cimg_instance
53294                                     "save_pfm(): Specified filename is (null).",
53295                                     cimg_instance);
53296       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53297       if (_depth>1)
53298         cimg::warn(_cimg_instance
53299                    "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
53300                    cimg_instance,
53301                    filename?filename:"(FILE*)");
53302       if (_spectrum>3)
53303         cimg::warn(_cimg_instance
53304                    "save_pfm(): image instance is multispectral, only the three first channels will be saved "
53305                    "in file '%s'.",
53306                    cimg_instance,
53307                    filename?filename:"(FILE*)");
53308 
53309       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53310       const T
53311         *ptr_r = data(0,0,0,0),
53312         *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
53313         *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
53314       const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
53315 
53316       std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
53317                    (_spectrum==1?'f':'F'),_width,_height);
53318 
53319       switch (_spectrum) {
53320       case 1 : { // Scalar image
53321         CImg<floatT> buf(buf_size);
53322         for (longT to_write = (longT)width()*height(); to_write>0; ) {
53323           const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size);
53324           float *ptrd = buf._data;
53325           for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
53326           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53327           cimg::fwrite(buf._data,N,nfile);
53328           to_write-=N;
53329         }
53330       } break;
53331       case 2 : { // RG image
53332         CImg<floatT> buf(buf_size);
53333         for (longT to_write = (longT)width()*height(); to_write>0; ) {
53334           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
53335           float *ptrd = buf._data;
53336           for (ulongT i = N; i>0; --i) {
53337             *(ptrd++) = (float)*(ptr_r++);
53338             *(ptrd++) = (float)*(ptr_g++);
53339             *(ptrd++) = 0;
53340           }
53341           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53342           cimg::fwrite(buf._data,3*N,nfile);
53343           to_write-=N;
53344         }
53345       } break;
53346       default : { // RGB image
53347         CImg<floatT> buf(buf_size);
53348         for (longT to_write = (longT)width()*height(); to_write>0; ) {
53349           const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
53350           float *ptrd = buf._data;
53351           for (ulongT i = N; i>0; --i) {
53352             *(ptrd++) = (float)*(ptr_r++);
53353             *(ptrd++) = (float)*(ptr_g++);
53354             *(ptrd++) = (float)*(ptr_b++);
53355           }
53356           if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
53357           cimg::fwrite(buf._data,3*N,nfile);
53358           to_write-=N;
53359         }
53360       }
53361       }
53362       if (!file) cimg::fclose(nfile);
53363       return *this;
53364     }
53365 
53366     //! Save image as a RGB file.
53367     /**
53368       \param filename Filename, as a C-string.
53369     **/
53370     const CImg<T>& save_rgb(const char *const filename) const {
53371       return _save_rgb(0,filename);
53372     }
53373 
53374     //! Save image as a RGB file \overloading.
53375     const CImg<T>& save_rgb(std::FILE *const file) const {
53376       return _save_rgb(file,0);
53377     }
53378 
53379     const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
53380       if (!file && !filename)
53381         throw CImgArgumentException(_cimg_instance
53382                                     "save_rgb(): Specified filename is (null).",
53383                                     cimg_instance);
53384       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53385       if (_spectrum!=3)
53386         cimg::warn(_cimg_instance
53387                    "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
53388                    cimg_instance,
53389                    filename?filename:"(FILE*)");
53390 
53391       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53392       const ulongT wh = (ulongT)_width*_height;
53393       unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
53394       const T
53395         *ptr1 = data(0,0,0,0),
53396         *ptr2 = _spectrum>1?data(0,0,0,1):0,
53397         *ptr3 = _spectrum>2?data(0,0,0,2):0;
53398       switch (_spectrum) {
53399       case 1 : { // Scalar image
53400         for (ulongT k = 0; k<wh; ++k) {
53401           const unsigned char val = (unsigned char)*(ptr1++);
53402           *(nbuffer++) = val;
53403           *(nbuffer++) = val;
53404           *(nbuffer++) = val;
53405         }
53406       } break;
53407       case 2 : { // RG image
53408         for (ulongT k = 0; k<wh; ++k) {
53409           *(nbuffer++) = (unsigned char)(*(ptr1++));
53410           *(nbuffer++) = (unsigned char)(*(ptr2++));
53411           *(nbuffer++) = 0;
53412         }
53413       } break;
53414       default : { // RGB image
53415         for (ulongT k = 0; k<wh; ++k) {
53416           *(nbuffer++) = (unsigned char)(*(ptr1++));
53417           *(nbuffer++) = (unsigned char)(*(ptr2++));
53418           *(nbuffer++) = (unsigned char)(*(ptr3++));
53419         }
53420       }
53421       }
53422       cimg::fwrite(buffer,3*wh,nfile);
53423       if (!file) cimg::fclose(nfile);
53424       delete[] buffer;
53425       return *this;
53426     }
53427 
53428     //! Save image as a RGBA file.
53429     /**
53430        \param filename Filename, as a C-string.
53431     **/
53432     const CImg<T>& save_rgba(const char *const filename) const {
53433       return _save_rgba(0,filename);
53434     }
53435 
53436     //! Save image as a RGBA file \overloading.
53437     const CImg<T>& save_rgba(std::FILE *const file) const {
53438       return _save_rgba(file,0);
53439     }
53440 
53441     const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
53442       if (!file && !filename)
53443         throw CImgArgumentException(_cimg_instance
53444                                     "save_rgba(): Specified filename is (null).",
53445                                     cimg_instance);
53446       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53447       if (_spectrum!=4)
53448         cimg::warn(_cimg_instance
53449                    "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
53450                    cimg_instance,
53451                    filename?filename:"(FILE*)");
53452 
53453       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53454       const ulongT wh = (ulongT)_width*_height;
53455       unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
53456       const T
53457         *ptr1 = data(0,0,0,0),
53458         *ptr2 = _spectrum>1?data(0,0,0,1):0,
53459         *ptr3 = _spectrum>2?data(0,0,0,2):0,
53460         *ptr4 = _spectrum>3?data(0,0,0,3):0;
53461       switch (_spectrum) {
53462       case 1 : { // Scalar images
53463         for (ulongT k = 0; k<wh; ++k) {
53464           const unsigned char val = (unsigned char)*(ptr1++);
53465           *(nbuffer++) = val;
53466           *(nbuffer++) = val;
53467           *(nbuffer++) = val;
53468           *(nbuffer++) = 255;
53469         }
53470       } break;
53471       case 2 : { // RG images
53472         for (ulongT k = 0; k<wh; ++k) {
53473           *(nbuffer++) = (unsigned char)(*(ptr1++));
53474           *(nbuffer++) = (unsigned char)(*(ptr2++));
53475           *(nbuffer++) = 0;
53476           *(nbuffer++) = 255;
53477         }
53478       } break;
53479       case 3 : { // RGB images
53480         for (ulongT k = 0; k<wh; ++k) {
53481           *(nbuffer++) = (unsigned char)(*(ptr1++));
53482           *(nbuffer++) = (unsigned char)(*(ptr2++));
53483           *(nbuffer++) = (unsigned char)(*(ptr3++));
53484           *(nbuffer++) = 255;
53485         }
53486       } break;
53487       default : { // RGBA images
53488         for (ulongT k = 0; k<wh; ++k) {
53489           *(nbuffer++) = (unsigned char)(*(ptr1++));
53490           *(nbuffer++) = (unsigned char)(*(ptr2++));
53491           *(nbuffer++) = (unsigned char)(*(ptr3++));
53492           *(nbuffer++) = (unsigned char)(*(ptr4++));
53493         }
53494       }
53495       }
53496       cimg::fwrite(buffer,4*wh,nfile);
53497       if (!file) cimg::fclose(nfile);
53498       delete[] buffer;
53499       return *this;
53500     }
53501 
53502     //! Save image as a TIFF file.
53503     /**
53504        \param filename Filename, as a C-string.
53505        \param compression_type Type of data compression. Can be <tt>{ 0=None | 1=LZW | 2=JPEG }</tt>.
53506        \param voxel_size Voxel size, to be stored in the filename.
53507        \param description Description, to be stored in the filename.
53508        \param use_bigtiff Allow to save big tiff files (>4Gb).
53509        \note
53510        - libtiff support is enabled by defining the precompilation
53511         directive \c cimg_use_tif.
53512        - When libtiff is enabled, 2D and 3D (multipage) several
53513         channel per pixel are supported for
53514         <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
53515        - If \c cimg_use_tif is not defined at compile time the
53516         function uses CImg<T>&save_other(const char*).
53517      **/
53518     const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
53519                              const float *const voxel_size=0, const char *const description=0,
53520                              const bool use_bigtiff=true) const {
53521       if (!filename)
53522         throw CImgArgumentException(_cimg_instance
53523                                     "save_tiff(): Specified filename is (null).",
53524                                     cimg_instance);
53525       if (is_empty()) { cimg::fempty(0,filename); return *this; }
53526 
53527 #ifdef cimg_use_tiff
53528       const bool
53529         _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images.
53530       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
53531       if (tif) {
53532         cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description);
53533         TIFFClose(tif);
53534       } else throw CImgIOException(_cimg_instance
53535                                    "save_tiff(): Failed to open file '%s' for writing.",
53536                                    cimg_instance,
53537                                    filename);
53538       return *this;
53539 #else
53540       cimg::unused(compression_type,voxel_size,description,use_bigtiff);
53541       return save_other(filename);
53542 #endif
53543     }
53544 
53545 #ifdef cimg_use_tiff
53546 
53547 #define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \
53548       const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); }
53549 
53550     // [internal] Save a plane into a tiff file
53551     template<typename t>
53552     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t,
53553                               const unsigned int compression_type, const float *const voxel_size,
53554                               const char *const description) const {
53555       if (is_empty() || !tif || pixel_t) return *this;
53556       const char *const filename = TIFFFileName(tif);
53557       uint32 rowsperstrip = (uint32)-1;
53558       uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
53559       if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
53560       else photometric = PHOTOMETRIC_MINISBLACK;
53561       TIFFSetDirectory(tif,directory);
53562       TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
53563       TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
53564       if (voxel_size) {
53565         const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2];
53566         TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE);
53567         TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.0f/vx);
53568         TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.0f/vy);
53569         CImg<charT> s_description(256);
53570         cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz);
53571         TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data());
53572       }
53573       if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description);
53574       TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
53575       TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
53576       if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
53577       else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
53578       else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
53579       double valm, valM = max_min(valm);
53580       TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm);
53581       TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM);
53582       TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
53583       TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
53584       TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
53585       TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG:
53586                    compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE);
53587       rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
53588       TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
53589       TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
53590       TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
53591 
53592       t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
53593       if (buf) {
53594         for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
53595           uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip);
53596           tstrip_t strip = TIFFComputeStrip(tif,row,0);
53597           tsize_t i = 0;
53598           for (unsigned int rr = 0; rr<nrow; ++rr)
53599             for (unsigned int cc = 0; cc<_width; ++cc)
53600               for (unsigned int vv = 0; vv<spp; ++vv)
53601                 buf[i++] = (t)(*this)(cc,row + rr,z,vv);
53602           if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
53603             throw CImgIOException(_cimg_instance
53604                                   "save_tiff(): Invalid strip writing when saving file '%s'.",
53605                                   cimg_instance,
53606                                   filename?filename:"(FILE*)");
53607         }
53608         _TIFFfree(buf);
53609       }
53610       TIFFWriteDirectory(tif);
53611       return *this;
53612     }
53613 
53614     const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z,
53615                               const unsigned int compression_type, const float *const voxel_size,
53616                               const char *const description) const {
53617       _cimg_save_tiff("bool",unsigned char,compression_type);
53618       _cimg_save_tiff("unsigned char",unsigned char,compression_type);
53619       _cimg_save_tiff("char",char,compression_type);
53620       _cimg_save_tiff("unsigned short",unsigned short,compression_type);
53621       _cimg_save_tiff("short",short,compression_type);
53622       _cimg_save_tiff("unsigned int",unsigned int,compression_type);
53623       _cimg_save_tiff("int",int,compression_type);
53624       _cimg_save_tiff("unsigned int64",unsigned int,compression_type);
53625       _cimg_save_tiff("int64",int,compression_type);
53626       _cimg_save_tiff("float",float,compression_type);
53627       _cimg_save_tiff("double",float,compression_type);
53628       const char *const filename = TIFFFileName(tif);
53629       throw CImgInstanceException(_cimg_instance
53630                                   "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
53631                                   cimg_instance,
53632                                   pixel_type(),filename?filename:"(FILE*)");
53633       return *this;
53634     }
53635 #endif
53636 
53637     //! Save image as a MINC2 file.
53638     /**
53639        \param filename Filename, as a C-string.
53640        \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from.
53641     **/
53642     const CImg<T>& save_minc2(const char *const filename,
53643                               const char *const imitate_file=0) const {
53644       if (!filename)
53645         throw CImgArgumentException(_cimg_instance
53646                                    "save_minc2(): Specified filename is (null).",
53647                                    cimg_instance);
53648       if (is_empty()) { cimg::fempty(0,filename); return *this; }
53649 
53650 #ifndef cimg_use_minc2
53651      cimg::unused(imitate_file);
53652      return save_other(filename);
53653 #else
53654      minc::minc_1_writer wtr;
53655      if (imitate_file)
53656        wtr.open(filename, imitate_file);
53657      else {
53658        minc::minc_info di;
53659        if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X));
53660        if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y));
53661        if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z));
53662        if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME));
53663        wtr.open(filename,di,1,NC_FLOAT,0);
53664      }
53665      if (cimg::type<T>::string()==cimg::type<unsigned char>::string())
53666        wtr.setup_write_byte();
53667      else if (cimg::type<T>::string()==cimg::type<int>::string())
53668        wtr.setup_write_int();
53669      else if (cimg::type<T>::string()==cimg::type<double>::string())
53670        wtr.setup_write_double();
53671      else
53672        wtr.setup_write_float();
53673      minc::save_standard_volume(wtr, this->_data);
53674      return *this;
53675 #endif
53676     }
53677 
53678     //! Save image as an ANALYZE7.5 or NIFTI file.
53679     /**
53680       \param filename Filename, as a C-string.
53681       \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions.
53682     **/
53683     const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
53684       if (!filename)
53685         throw CImgArgumentException(_cimg_instance
53686                                     "save_analyze(): Specified filename is (null).",
53687                                     cimg_instance);
53688       if (is_empty()) { cimg::fempty(0,filename); return *this; }
53689 
53690       std::FILE *file;
53691       CImg<charT> hname(1024), iname(1024);
53692       const char *const ext = cimg::split_filename(filename);
53693       short datatype = -1;
53694       if (!*ext) {
53695         cimg_snprintf(hname,hname._width,"%s.hdr",filename);
53696         cimg_snprintf(iname,iname._width,"%s.img",filename);
53697       }
53698       if (!cimg::strncasecmp(ext,"hdr",3)) {
53699         std::strcpy(hname,filename);
53700         std::strncpy(iname,filename,iname._width - 1);
53701         cimg_sprintf(iname._data + std::strlen(iname) - 3,"img");
53702       }
53703       if (!cimg::strncasecmp(ext,"img",3)) {
53704         std::strcpy(hname,filename);
53705         std::strncpy(iname,filename,iname._width - 1);
53706         cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr");
53707       }
53708       if (!cimg::strncasecmp(ext,"nii",3)) {
53709         std::strncpy(hname,filename,hname._width - 1); *iname = 0;
53710       }
53711 
53712       CImg<charT> header(*iname?348:352,1,1,1,0);
53713       int *const iheader = (int*)header._data;
53714       *iheader = 348;
53715       std::strcpy(header._data + 4,"CImg");
53716       std::strcpy(header._data + 14," ");
53717       ((short*)&(header[36]))[0] = 4096;
53718       ((char*)&(header[38]))[0] = 114;
53719       ((short*)&(header[40]))[0] = 4;
53720       ((short*)&(header[40]))[1] = (short)_width;
53721       ((short*)&(header[40]))[2] = (short)_height;
53722       ((short*)&(header[40]))[3] = (short)_depth;
53723       ((short*)&(header[40]))[4] = (short)_spectrum;
53724       if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
53725       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
53726       if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
53727       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
53728       if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
53729       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
53730       if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
53731       if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8;
53732       if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8;
53733       if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
53734       if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
53735       if (datatype<0)
53736         throw CImgIOException(_cimg_instance
53737                               "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
53738                               cimg_instance,
53739                               pixel_type(),filename);
53740 
53741       ((short*)&(header[70]))[0] = datatype;
53742       ((short*)&(header[72]))[0] = sizeof(T);
53743       ((float*)&(header[108]))[0] = (float)(*iname?0:header.width());
53744       ((float*)&(header[112]))[0] = 1;
53745       ((float*)&(header[76]))[0] = 0;
53746       if (voxel_size) {
53747         ((float*)&(header[76]))[1] = voxel_size[0];
53748         ((float*)&(header[76]))[2] = voxel_size[1];
53749         ((float*)&(header[76]))[3] = voxel_size[2];
53750       } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1;
53751       file = cimg::fopen(hname,"wb");
53752       cimg::fwrite(header._data,header.width(),file);
53753       if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
53754       cimg::fwrite(_data,size(),file);
53755       cimg::fclose(file);
53756       return *this;
53757     }
53758 
53759     //! Save image as a .cimg file.
53760     /**
53761       \param filename Filename, as a C-string.
53762       \param is_compressed Tells if the file contains compressed image data.
53763     **/
53764     const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
53765       CImgList<T>(*this,true).save_cimg(filename,is_compressed);
53766       return *this;
53767     }
53768 
53769     //! Save image as a .cimg file \overloading.
53770     const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
53771       CImgList<T>(*this,true).save_cimg(file,is_compressed);
53772       return *this;
53773     }
53774 
53775     //! Save image as a sub-image into an existing .cimg file.
53776     /**
53777       \param filename Filename, as a C-string.
53778       \param n0 Index of the image inside the file.
53779       \param x0 X-coordinate of the sub-image location.
53780       \param y0 Y-coordinate of the sub-image location.
53781       \param z0 Z-coordinate of the sub-image location.
53782       \param c0 C-coordinate of the sub-image location.
53783     **/
53784     const CImg<T>& save_cimg(const char *const filename,
53785                              const unsigned int n0,
53786                              const unsigned int x0, const unsigned int y0,
53787                              const unsigned int z0, const unsigned int c0) const {
53788       CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
53789       return *this;
53790     }
53791 
53792     //! Save image as a sub-image into an existing .cimg file \overloading.
53793     const CImg<T>& save_cimg(std::FILE *const file,
53794 			     const unsigned int n0,
53795 			     const unsigned int x0, const unsigned int y0,
53796 			     const unsigned int z0, const unsigned int c0) const {
53797       CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
53798       return *this;
53799     }
53800 
53801     //! Save blank image as a .cimg file.
53802     /**
53803         \param filename Filename, as a C-string.
53804         \param dx Width of the image.
53805         \param dy Height of the image.
53806         \param dz Depth of the image.
53807         \param dc Number of channels of the image.
53808         \note
53809         - All pixel values of the saved image are set to \c 0.
53810         - Use this method to save large images without having to instanciate and allocate them.
53811     **/
53812     static void save_empty_cimg(const char *const filename,
53813                                 const unsigned int dx, const unsigned int dy=1,
53814                                 const unsigned int dz=1, const unsigned int dc=1) {
53815       return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
53816     }
53817 
53818     //! Save blank image as a .cimg file \overloading.
53819     /**
53820        Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int)
53821        with a file stream argument instead of a filename string.
53822     **/
53823     static void save_empty_cimg(std::FILE *const file,
53824                                 const unsigned int dx, const unsigned int dy=1,
53825                                 const unsigned int dz=1, const unsigned int dc=1) {
53826       return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
53827     }
53828 
53829     //! Save image as an INRIMAGE-4 file.
53830     /**
53831       \param filename Filename, as a C-string.
53832       \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions.
53833     **/
53834     const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
53835       return _save_inr(0,filename,voxel_size);
53836     }
53837 
53838     //! Save image as an INRIMAGE-4 file \overloading.
53839     const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
53840       return _save_inr(file,0,voxel_size);
53841     }
53842 
53843     const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
53844       if (!file && !filename)
53845         throw CImgArgumentException(_cimg_instance
53846                                     "save_inr(): Specified filename is (null).",
53847                                     cimg_instance);
53848       if (is_empty()) { cimg::fempty(file,filename); return *this; }
53849 
53850       int inrpixsize = -1;
53851       const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
53852       if (!cimg::strcasecmp(pixel_type(),"unsigned char")) {
53853         inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
53854       }
53855       if (!cimg::strcasecmp(pixel_type(),"char")) {
53856         inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
53857       }
53858       if (!cimg::strcasecmp(pixel_type(),"unsigned short")) {
53859         inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2;
53860       }
53861       if (!cimg::strcasecmp(pixel_type(),"short")) {
53862         inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2;
53863       }
53864       if (!cimg::strcasecmp(pixel_type(),"unsigned int")) {
53865         inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4;
53866       }
53867       if (!cimg::strcasecmp(pixel_type(),"int")) {
53868         inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4;
53869       }
53870       if (!cimg::strcasecmp(pixel_type(),"float")) {
53871         inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4;
53872       }
53873       if (!cimg::strcasecmp(pixel_type(),"double")) {
53874         inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8;
53875       }
53876       if (inrpixsize<=0)
53877         throw CImgIOException(_cimg_instance
53878                               "save_inr(): Unsupported pixel type '%s' for file '%s'",
53879                               cimg_instance,
53880                               pixel_type(),filename?filename:"(FILE*)");
53881 
53882       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
53883       CImg<charT> header(257);
53884       int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",
53885                               _width,_height,_depth,_spectrum);
53886       if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n",
53887                                         voxel_size[0],voxel_size[1],voxel_size[2]);
53888       err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
53889       std::memset(header._data + err,'\n',252 - err);
53890       std::memcpy(header._data + 252,"##}\n",4);
53891       cimg::fwrite(header._data,256,nfile);
53892       cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
53893       if (!file) cimg::fclose(nfile);
53894       return *this;
53895     }
53896 
53897     //! Save image as an OpenEXR file.
53898     /**
53899        \param filename Filename, as a C-string.
53900        \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>.
53901     **/
53902     const CImg<T>& save_exr(const char *const filename) const {
53903       if (!filename)
53904         throw CImgArgumentException(_cimg_instance
53905                                     "save_exr(): Specified filename is (null).",
53906                                     cimg_instance);
53907       if (is_empty()) { cimg::fempty(0,filename); return *this; }
53908       if (_depth>1)
53909         cimg::warn(_cimg_instance
53910                    "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
53911                    cimg_instance,
53912                    filename);
53913 
53914 #ifndef cimg_use_openexr
53915       return save_other(filename);
53916 #else
53917       Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba;
53918       switch (_spectrum) {
53919       case 1 : { // Grayscale image.
53920         for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
53921           rgba.r = rgba.g = rgba.b = (half)(*(ptr_r++));
53922           rgba.a = (half)1;
53923           *(ptrd++) = rgba;
53924         }
53925       } break;
53926       case 2 : { // RG image.
53927         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1),
53928                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e; ) {
53929           rgba.r = (half)(*(ptr_r++));
53930           rgba.g = (half)(*(ptr_g++));
53931           rgba.b = (half)0;
53932           rgba.a = (half)1;
53933           *(ptrd++) = rgba;
53934         }
53935       } break;
53936       case 3 : { // RGB image.
53937         for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2),
53938                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
53939           rgba.r = (half)(*(ptr_r++));
53940           rgba.g = (half)(*(ptr_g++));
53941           rgba.b = (half)(*(ptr_b++));
53942           rgba.a = (half)1;
53943           *(ptrd++) = rgba;
53944         }
53945       } break;
53946       default : { // RGBA image.
53947         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),
53948                *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
53949           rgba.r = (half)(*(ptr_r++));
53950           rgba.g = (half)(*(ptr_g++));
53951           rgba.b = (half)(*(ptr_b++));
53952           rgba.a = (half)(*(ptr_a++));
53953           *(ptrd++) = rgba;
53954         }
53955       } break;
53956       }
53957       Imf::RgbaOutputFile outFile(filename,_width,_height,
53958                                   _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?
53959                                   Imf::WRITE_RGB:Imf::WRITE_RGBA);
53960       outFile.setFrameBuffer(ptrd0,1,_width);
53961       outFile.writePixels(_height);
53962       delete[] ptrd0;
53963       return *this;
53964 #endif
53965     }
53966 
53967     //! Save image as a Pandore-5 file.
53968     /**
53969        \param filename Filename, as a C-string.
53970        \param colorspace Colorspace data field in output file
53971        (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore">Pandore file specifications</a>
53972        for more information).
53973     **/
53974     const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
53975       return _save_pandore(0,filename,colorspace);
53976     }
53977 
53978     //! Save image as a Pandore-5 file \overloading.
53979     /**
53980         Same as save_pandore(const char *,unsigned int) const
53981         with a file stream argument instead of a filename string.
53982     **/
53983     const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
53984       return _save_pandore(file,0,colorspace);
53985     }
53986 
53987     unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
53988       unsigned int nbdims = 0;
53989       if (id==2 || id==3 || id==4) {
53990         dims[0] = 1; dims[1] = _width; nbdims = 2;
53991       }
53992       if (id==5 || id==6 || id==7) {
53993         dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3;
53994       }
53995       if (id==8 || id==9 || id==10) {
53996         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
53997       }
53998       if (id==16 || id==17 || id==18) {
53999         dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4;
54000       }
54001       if (id==19 || id==20 || id==21) {
54002         dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5;
54003       }
54004       if (id==22 || id==23 || id==25) {
54005         dims[0] = _spectrum; dims[1] = _width; nbdims = 2;
54006       }
54007       if (id==26 || id==27 || id==29) {
54008         dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3;
54009       }
54010       if (id==30 || id==31 || id==33) {
54011         dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
54012       }
54013       return nbdims;
54014     }
54015 
54016     const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename,
54017                                  const unsigned int colorspace) const {
54018 
54019 #define __cimg_save_pandore_case(dtype) \
54020        dtype *buffer = new dtype[size()]; \
54021        const T *ptrs = _data; \
54022        cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
54023        buffer-=size(); \
54024        cimg::fwrite(buffer,size(),nfile); \
54025        delete[] buffer
54026 
54027 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
54028       if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \
54029           (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
54030 	unsigned int *iheader = (unsigned int*)(header + 12); \
54031 	nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
54032 	cimg::fwrite(header,36,nfile); \
54033         if (sizeof(unsigned long)==4) { CImg<ulongT> ndims(5); \
54034           for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \
54035         else if (sizeof(unsigned int)==4) { CImg<uintT> ndims(5); \
54036           for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \
54037         else if (sizeof(unsigned short)==4) { CImg<ushortT> ndims(5); \
54038           for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims._data,nbdims,nfile); } \
54039         else throw CImgIOException(_cimg_instance \
54040                                    "save_pandore(): Unsupported datatype for file '%s'.",\
54041                                    cimg_instance, \
54042                                    filename?filename:"(FILE*)"); \
54043 	if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
54044           __cimg_save_pandore_case(unsigned char); \
54045 	} else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
54046           if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
54047           else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
54048           else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
54049           else throw CImgIOException(_cimg_instance \
54050                                      "save_pandore(): Unsupported datatype for file '%s'.",\
54051                                      cimg_instance, \
54052                                      filename?filename:"(FILE*)"); \
54053 	} else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
54054           if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
54055           else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
54056           else throw CImgIOException(_cimg_instance \
54057                                      "save_pandore(): Unsupported datatype for file '%s'.",\
54058                                      cimg_instance, \
54059                                      filename?filename:"(FILE*)"); \
54060         } \
54061 	saved = true; \
54062       }
54063 
54064       if (!file && !filename)
54065         throw CImgArgumentException(_cimg_instance
54066                                     "save_pandore(): Specified filename is (null).",
54067                                     cimg_instance);
54068       if (is_empty()) { cimg::fempty(file,filename); return *this; }
54069 
54070       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
54071       unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
54072                                    0,0,0,0,'C','I','m','g',0,0,0,0,0,
54073                                    'N','o',' ','d','a','t','e',0,0,0,0 };
54074       unsigned int nbdims, dims[5] = { 0 };
54075       bool saved = false;
54076       _cimg_save_pandore_case(1,1,1,"unsigned char",2);
54077       _cimg_save_pandore_case(1,1,1,"char",3);
54078       _cimg_save_pandore_case(1,1,1,"unsigned short",3);
54079       _cimg_save_pandore_case(1,1,1,"short",3);
54080       _cimg_save_pandore_case(1,1,1,"unsigned int",3);
54081       _cimg_save_pandore_case(1,1,1,"int",3);
54082       _cimg_save_pandore_case(1,1,1,"unsigned int64",3);
54083       _cimg_save_pandore_case(1,1,1,"int64",3);
54084       _cimg_save_pandore_case(1,1,1,"float",4);
54085       _cimg_save_pandore_case(1,1,1,"double",4);
54086 
54087       _cimg_save_pandore_case(0,1,1,"unsigned char",5);
54088       _cimg_save_pandore_case(0,1,1,"char",6);
54089       _cimg_save_pandore_case(0,1,1,"unsigned short",6);
54090       _cimg_save_pandore_case(0,1,1,"short",6);
54091       _cimg_save_pandore_case(0,1,1,"unsigned int",6);
54092       _cimg_save_pandore_case(0,1,1,"int",6);
54093       _cimg_save_pandore_case(0,1,1,"unsigned int64",6);
54094       _cimg_save_pandore_case(0,1,1,"int64",6);
54095       _cimg_save_pandore_case(0,1,1,"float",7);
54096       _cimg_save_pandore_case(0,1,1,"double",7);
54097 
54098       _cimg_save_pandore_case(0,0,1,"unsigned char",8);
54099       _cimg_save_pandore_case(0,0,1,"char",9);
54100       _cimg_save_pandore_case(0,0,1,"unsigned short",9);
54101       _cimg_save_pandore_case(0,0,1,"short",9);
54102       _cimg_save_pandore_case(0,0,1,"unsigned int",9);
54103       _cimg_save_pandore_case(0,0,1,"int",9);
54104       _cimg_save_pandore_case(0,0,1,"unsigned int64",9);
54105       _cimg_save_pandore_case(0,0,1,"int64",9);
54106       _cimg_save_pandore_case(0,0,1,"float",10);
54107       _cimg_save_pandore_case(0,0,1,"double",10);
54108 
54109       _cimg_save_pandore_case(0,1,3,"unsigned char",16);
54110       _cimg_save_pandore_case(0,1,3,"char",17);
54111       _cimg_save_pandore_case(0,1,3,"unsigned short",17);
54112       _cimg_save_pandore_case(0,1,3,"short",17);
54113       _cimg_save_pandore_case(0,1,3,"unsigned int",17);
54114       _cimg_save_pandore_case(0,1,3,"int",17);
54115       _cimg_save_pandore_case(0,1,3,"unsigned int64",17);
54116       _cimg_save_pandore_case(0,1,3,"int64",17);
54117       _cimg_save_pandore_case(0,1,3,"float",18);
54118       _cimg_save_pandore_case(0,1,3,"double",18);
54119 
54120       _cimg_save_pandore_case(0,0,3,"unsigned char",19);
54121       _cimg_save_pandore_case(0,0,3,"char",20);
54122       _cimg_save_pandore_case(0,0,3,"unsigned short",20);
54123       _cimg_save_pandore_case(0,0,3,"short",20);
54124       _cimg_save_pandore_case(0,0,3,"unsigned int",20);
54125       _cimg_save_pandore_case(0,0,3,"int",20);
54126       _cimg_save_pandore_case(0,0,3,"unsigned int64",20);
54127       _cimg_save_pandore_case(0,0,3,"int64",20);
54128       _cimg_save_pandore_case(0,0,3,"float",21);
54129       _cimg_save_pandore_case(0,0,3,"double",21);
54130 
54131       _cimg_save_pandore_case(1,1,0,"unsigned char",22);
54132       _cimg_save_pandore_case(1,1,0,"char",23);
54133       _cimg_save_pandore_case(1,1,0,"unsigned short",23);
54134       _cimg_save_pandore_case(1,1,0,"short",23);
54135       _cimg_save_pandore_case(1,1,0,"unsigned int",23);
54136       _cimg_save_pandore_case(1,1,0,"int",23);
54137       _cimg_save_pandore_case(1,1,0,"unsigned int64",23);
54138       _cimg_save_pandore_case(1,1,0,"int64",23);
54139       _cimg_save_pandore_case(1,1,0,"float",25);
54140       _cimg_save_pandore_case(1,1,0,"double",25);
54141 
54142       _cimg_save_pandore_case(0,1,0,"unsigned char",26);
54143       _cimg_save_pandore_case(0,1,0,"char",27);
54144       _cimg_save_pandore_case(0,1,0,"unsigned short",27);
54145       _cimg_save_pandore_case(0,1,0,"short",27);
54146       _cimg_save_pandore_case(0,1,0,"unsigned int",27);
54147       _cimg_save_pandore_case(0,1,0,"int",27);
54148       _cimg_save_pandore_case(0,1,0,"unsigned int64",27);
54149       _cimg_save_pandore_case(0,1,0,"int64",27);
54150       _cimg_save_pandore_case(0,1,0,"float",29);
54151       _cimg_save_pandore_case(0,1,0,"double",29);
54152 
54153       _cimg_save_pandore_case(0,0,0,"unsigned char",30);
54154       _cimg_save_pandore_case(0,0,0,"char",31);
54155       _cimg_save_pandore_case(0,0,0,"unsigned short",31);
54156       _cimg_save_pandore_case(0,0,0,"short",31);
54157       _cimg_save_pandore_case(0,0,0,"unsigned int",31);
54158       _cimg_save_pandore_case(0,0,0,"int",31);
54159       _cimg_save_pandore_case(0,0,0,"unsigned int64",31);
54160       _cimg_save_pandore_case(0,0,0,"int64",31);
54161       _cimg_save_pandore_case(0,0,0,"float",33);
54162       _cimg_save_pandore_case(0,0,0,"double",33);
54163 
54164       if (!file) cimg::fclose(nfile);
54165       return *this;
54166     }
54167 
54168     //! Save image as a raw data file.
54169     /**
54170        \param filename Filename, as a C-string.
54171        \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false).
54172        \note The .raw format does not store the image dimensions in the output file,
54173        so you have to keep track of them somewhere to be able to read the file correctly afterwards.
54174     **/
54175     const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
54176       return _save_raw(0,filename,is_multiplexed);
54177     }
54178 
54179     //! Save image as a raw data file \overloading.
54180     /**
54181        Same as save_raw(const char *,bool) const
54182        with a file stream argument instead of a filename string.
54183     **/
54184     const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
54185       return _save_raw(file,0,is_multiplexed);
54186     }
54187 
54188     const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
54189       if (!file && !filename)
54190         throw CImgArgumentException(_cimg_instance
54191                                     "save_raw(): Specified filename is (null).",
54192                                     cimg_instance);
54193       if (is_empty()) { cimg::fempty(file,filename); return *this; }
54194 
54195       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
54196       if (!is_multiplexed) cimg::fwrite(_data,size(),nfile);
54197       else {
54198         CImg<T> buf(_spectrum);
54199         cimg_forXYZ(*this,x,y,z) {
54200           cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
54201           cimg::fwrite(buf._data,_spectrum,nfile);
54202         }
54203       }
54204       if (!file) cimg::fclose(nfile);
54205       return *this;
54206     }
54207 
54208     //! Save image as a .yuv video file.
54209     /**
54210        \param filename Filename, as a C-string.
54211        \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
54212        \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false).
54213        \note Each slice of the instance image is considered to be a single frame of the output video file.
54214     **/
54215     const CImg<T>& save_yuv(const char *const filename,
54216                             const unsigned int chroma_subsampling=444,
54217                             const bool is_rgb=true) const {
54218       CImgList<T>(*this,true).save_yuv(filename,chroma_subsampling,is_rgb);
54219       return *this;
54220     }
54221 
54222     //! Save image as a .yuv video file \overloading.
54223     /**
54224        Same as save_yuv(const char*,const unsigned int,const bool) const
54225        with a file stream argument instead of a filename string.
54226     **/
54227     const CImg<T>& save_yuv(std::FILE *const file,
54228                             const unsigned int chroma_subsampling=444,
54229                             const bool is_rgb=true) const {
54230       CImgList<T>(*this,true).save_yuv(file,chroma_subsampling,is_rgb);
54231       return *this;
54232     }
54233 
54234     //! Save 3d object as an Object File Format (.off) file.
54235     /**
54236        \param filename Filename, as a C-string.
54237        \param primitives List of 3d object primitives.
54238        \param colors List of 3d object colors.
54239        \note
54240        - Instance image contains the vertices data of the 3d object.
54241        - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format.
54242        Such primitives will be lost or simplified during file saving.
54243        - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>.
54244     **/
54245     template<typename tf, typename tc>
54246     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
54247                             const char *const filename) const {
54248       return _save_off(primitives,colors,0,filename);
54249     }
54250 
54251     //! Save 3d object as an Object File Format (.off) file \overloading.
54252     /**
54253        Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const
54254        with a file stream argument instead of a filename string.
54255     **/
54256     template<typename tf, typename tc>
54257     const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
54258                             std::FILE *const file) const {
54259       return _save_off(primitives,colors,file,0);
54260     }
54261 
54262     template<typename tf, typename tc>
54263     const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
54264                              std::FILE *const file, const char *const filename) const {
54265       if (!file && !filename)
54266         throw CImgArgumentException(_cimg_instance
54267                                     "save_off(): Specified filename is (null).",
54268                                     cimg_instance);
54269       if (is_empty())
54270         throw CImgInstanceException(_cimg_instance
54271                                     "save_off(): Empty instance, for file '%s'.",
54272                                     cimg_instance,
54273                                     filename?filename:"(FILE*)");
54274 
54275       CImgList<T> opacities;
54276       CImg<charT> error_message(1024);
54277       if (!is_object3d(primitives,colors,opacities,true,error_message))
54278         throw CImgInstanceException(_cimg_instance
54279                                     "save_off(): Invalid specified 3d object, for file '%s' (%s).",
54280                                     cimg_instance,
54281                                     filename?filename:"(FILE*)",error_message.data());
54282 
54283       const CImg<tc> default_color(1,3,1,1,200);
54284       std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
54285       unsigned int supported_primitives = 0;
54286       cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
54287       std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
54288       cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",
54289                                       (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
54290       cimglist_for(primitives,l) {
54291         const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
54292         const unsigned int psiz = primitives[l].size(), csiz = color.size();
54293         const float r = color[0]/255.0f, g = (csiz>1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f;
54294         switch (psiz) {
54295         case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",
54296                               (unsigned int)primitives(l,0),r,g,b); break;
54297         case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
54298                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
54299         case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
54300                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
54301                               (unsigned int)primitives(l,1),r,g,b); break;
54302         case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
54303                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
54304                               (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
54305         case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
54306                               (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
54307         case 6 : {
54308           const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
54309           const float
54310             rt = color.atXY(xt,yt,0)/255.0f,
54311             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
54312             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
54313           std::fprintf(nfile,"2 %u %u %f %f %f\n",
54314                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
54315         } break;
54316         case 9 : {
54317           const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
54318           const float
54319             rt = color.atXY(xt,yt,0)/255.0f,
54320             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
54321             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
54322           std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
54323                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
54324                        (unsigned int)primitives(l,1),rt,gt,bt);
54325         } break;
54326         case 12 : {
54327           const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
54328           const float
54329             rt = color.atXY(xt,yt,0)/255.0f,
54330             gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f,
54331             bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
54332           std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
54333                        (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
54334                        (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
54335         } break;
54336         }
54337       }
54338       if (!file) cimg::fclose(nfile);
54339       return *this;
54340     }
54341 
54342     //! Save volumetric image as a video, using the OpenCV library.
54343     /**
54344       \param filename Filename to write data to.
54345       \param fps Number of frames per second.
54346       \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
54347       \param keep_open Tells if the video writer associated to the specified filename
54348         must be kept open or not (to allow frames to be added in the same file afterwards).
54349     **/
54350     const CImg<T>& save_video(const char *const filename, const unsigned int fps=25,
54351                               const char *codec=0, const bool keep_open=false) const {
54352       if (is_empty()) { CImgList<T>().save_video(filename,fps,codec,keep_open); return *this; }
54353       CImgList<T> list;
54354       get_split('z').move_to(list);
54355       list.save_video(filename,fps,codec,keep_open);
54356       return *this;
54357     }
54358 
54359     //! Save volumetric image as a video, using ffmpeg external binary.
54360     /**
54361        \param filename Filename, as a C-string.
54362        \param fps Video framerate.
54363        \param codec Video codec, as a C-string.
54364        \param bitrate Video bitrate.
54365        \note
54366        - Each slice of the instance image is considered to be a single frame of the output video file.
54367        - This method uses \c ffmpeg, an external executable binary provided by
54368          <a href="http://www.ffmpeg.org">FFmpeg</a>.
54369        It must be installed for the method to succeed.
54370     **/
54371     const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
54372                                         const char *const codec=0, const unsigned int bitrate=2048) const {
54373       if (!filename)
54374         throw CImgArgumentException(_cimg_instance
54375                                     "save_ffmpeg_external(): Specified filename is (null).",
54376                                     cimg_instance);
54377       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54378 
54379       CImgList<T> list;
54380       get_split('z').move_to(list);
54381       list.save_ffmpeg_external(filename,fps,codec,bitrate);
54382       return *this;
54383     }
54384 
54385     //! Save image using gzip external binary.
54386     /**
54387        \param filename Filename, as a C-string.
54388        \note This method uses \c gzip, an external executable binary provided by
54389          <a href="//http://www.gzip.org">gzip</a>.
54390        It must be installed for the method to succeed.
54391     **/
54392     const CImg<T>& save_gzip_external(const char *const filename) const {
54393       if (!filename)
54394         throw CImgArgumentException(_cimg_instance
54395                                     "save_gzip_external(): Specified filename is (null).",
54396                                     cimg_instance);
54397       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54398 
54399       CImg<charT> command(1024), filename_tmp(256), body(256);
54400       const char
54401         *ext = cimg::split_filename(filename,body),
54402         *ext2 = cimg::split_filename(body,0);
54403       std::FILE *file;
54404       do {
54405         if (!cimg::strcasecmp(ext,"gz")) {
54406           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54407                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
54408           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
54409                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54410         } else {
54411           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54412                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
54413           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
54414                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
54415         }
54416         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54417       } while (file);
54418       save(filename_tmp);
54419       cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
54420                     cimg::gzip_path(),
54421                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
54422                     CImg<charT>::string(filename)._system_strescape().data());
54423       cimg::system(command);
54424       file = std_fopen(filename,"rb");
54425       if (!file)
54426         throw CImgIOException(_cimg_instance
54427                               "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
54428                               cimg_instance,
54429                               filename);
54430 
54431       else cimg::fclose(file);
54432       std::remove(filename_tmp);
54433       return *this;
54434     }
54435 
54436     //! Save image using GraphicsMagick's external binary.
54437     /**
54438        \param filename Filename, as a C-string.
54439        \param quality Image quality (expressed in percent), when the file format supports it.
54440        \note This method uses \c gm, an external executable binary provided by
54441          <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
54442        It must be installed for the method to succeed.
54443     **/
54444     const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
54445       if (!filename)
54446         throw CImgArgumentException(_cimg_instance
54447                                     "save_graphicsmagick_external(): Specified filename is (null).",
54448                                     cimg_instance);
54449       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54450       if (_depth>1)
54451         cimg::warn(_cimg_instance
54452                    "save_other(): File '%s', saving a volumetric image with an external call to "
54453                    "GraphicsMagick only writes the first image slice.",
54454                    cimg_instance,filename);
54455 
54456 #ifdef cimg_use_png
54457 #define _cimg_sge_ext1 "png"
54458 #define _cimg_sge_ext2 "png"
54459 #else
54460 #define _cimg_sge_ext1 "pgm"
54461 #define _cimg_sge_ext2 "ppm"
54462 #endif
54463       CImg<charT> command(1024), filename_tmp(256);
54464       std::FILE *file;
54465       do {
54466         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
54467                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),
54468                       _spectrum==1?_cimg_sge_ext1:_cimg_sge_ext2);
54469         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54470       } while (file);
54471 #ifdef cimg_use_png
54472       save_png(filename_tmp);
54473 #else
54474       save_pnm(filename_tmp);
54475 #endif
54476       cimg_snprintf(command,command._width,"%s convert -quality %u \"%s\" \"%s\"",
54477                     cimg::graphicsmagick_path(),quality,
54478                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
54479                     CImg<charT>::string(filename)._system_strescape().data());
54480       cimg::system(command);
54481       file = std_fopen(filename,"rb");
54482       if (!file)
54483         throw CImgIOException(_cimg_instance
54484                               "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
54485                               cimg_instance,
54486                               filename);
54487 
54488       if (file) cimg::fclose(file);
54489       std::remove(filename_tmp);
54490       return *this;
54491     }
54492 
54493     //! Save image using ImageMagick's external binary.
54494     /**
54495        \param filename Filename, as a C-string.
54496        \param quality Image quality (expressed in percent), when the file format supports it.
54497        \note This method uses \c convert, an external executable binary provided by
54498        <a href="http://www.imagemagick.org">ImageMagick</a>.
54499        It must be installed for the method to succeed.
54500     **/
54501     const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
54502       if (!filename)
54503         throw CImgArgumentException(_cimg_instance
54504                                     "save_imagemagick_external(): Specified filename is (null).",
54505                                     cimg_instance);
54506       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54507       if (_depth>1)
54508         cimg::warn(_cimg_instance
54509                    "save_other(): File '%s', saving a volumetric image with an external call to "
54510                    "ImageMagick only writes the first image slice.",
54511                    cimg_instance,filename);
54512 #ifdef cimg_use_png
54513 #define _cimg_sie_ext1 "png"
54514 #define _cimg_sie_ext2 "png"
54515 #else
54516 #define _cimg_sie_ext1 "pgm"
54517 #define _cimg_sie_ext2 "ppm"
54518 #endif
54519       CImg<charT> command(1024), filename_tmp(256);
54520       std::FILE *file;
54521       do {
54522         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(),
54523                       cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_ext1:_cimg_sie_ext2);
54524         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54525       } while (file);
54526 #ifdef cimg_use_png
54527       save_png(filename_tmp);
54528 #else
54529       save_pnm(filename_tmp);
54530 #endif
54531       cimg_snprintf(command,command._width,"%s -quality %u \"%s\" \"%s\"",
54532                     cimg::imagemagick_path(),quality,
54533                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
54534                     CImg<charT>::string(filename)._system_strescape().data());
54535       cimg::system(command);
54536       file = std_fopen(filename,"rb");
54537       if (!file)
54538         throw CImgIOException(_cimg_instance
54539                               "save_imagemagick_external(): Failed to save file '%s' with "
54540                               "external command 'magick/convert'.",
54541                               cimg_instance,
54542                               filename);
54543 
54544       if (file) cimg::fclose(file);
54545       std::remove(filename_tmp);
54546       return *this;
54547     }
54548 
54549     //! Save image as a Dicom file.
54550     /**
54551        \param filename Filename, as a C-string.
54552        \note This method uses \c medcon, an external executable binary provided by
54553          <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>.
54554        It must be installed for the method to succeed.
54555     **/
54556     const CImg<T>& save_medcon_external(const char *const filename) const {
54557       if (!filename)
54558         throw CImgArgumentException(_cimg_instance
54559                                     "save_medcon_external(): Specified filename is (null).",
54560                                     cimg_instance);
54561       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54562 
54563       CImg<charT> command(1024), filename_tmp(256), body(256);
54564       std::FILE *file;
54565       do {
54566         cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
54567         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
54568       } while (file);
54569       save_analyze(filename_tmp);
54570       cimg_snprintf(command,command._width,"%s -w -c dicom -o \"%s\" -f \"%s\"",
54571                     cimg::medcon_path(),
54572                     CImg<charT>::string(filename)._system_strescape().data(),
54573                     CImg<charT>::string(filename_tmp)._system_strescape().data());
54574       cimg::system(command);
54575       std::remove(filename_tmp);
54576       cimg::split_filename(filename_tmp,body);
54577       cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data);
54578       std::remove(filename_tmp);
54579 
54580       file = std_fopen(filename,"rb");
54581       if (!file) {
54582         cimg_snprintf(command,command._width,"m000-%s",filename);
54583         file = std_fopen(command,"rb");
54584         if (!file) {
54585           cimg::fclose(cimg::fopen(filename,"r"));
54586           throw CImgIOException(_cimg_instance
54587                                 "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
54588                                 cimg_instance,
54589                                 filename);
54590         }
54591       }
54592       cimg::fclose(file);
54593       std::rename(command,filename);
54594       return *this;
54595     }
54596 
54597     // Save image for non natively supported formats.
54598     /**
54599        \param filename Filename, as a C-string.
54600        \param quality Image quality (expressed in percent), when the file format supports it.
54601        \note
54602        - The filename extension tells about the desired file format.
54603        - This method tries to save the instance image as a file, using external tools from
54604        <a href="http://www.imagemagick.org">ImageMagick</a> or
54605        <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
54606          At least one of these tool must be installed for the method to succeed.
54607        - It is recommended to use the generic method save(const char*, int) const instead,
54608          as it can handle some file formats natively.
54609     **/
54610     const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
54611       if (!filename)
54612         throw CImgArgumentException(_cimg_instance
54613                                     "save_other(): Specified filename is (null).",
54614                                     cimg_instance);
54615       if (is_empty()) { cimg::fempty(0,filename); return *this; }
54616       if (_depth>1)
54617         cimg::warn(_cimg_instance
54618                    "save_other(): File '%s', saving a volumetric image with an external call to "
54619                    "ImageMagick or GraphicsMagick only writes the first image slice.",
54620                    cimg_instance,filename);
54621 
54622       const unsigned int omode = cimg::exception_mode();
54623       bool is_saved = true;
54624       cimg::exception_mode(0);
54625       try { save_magick(filename); }
54626       catch (CImgException&) {
54627         try { save_imagemagick_external(filename,quality); }
54628         catch (CImgException&) {
54629           try { save_graphicsmagick_external(filename,quality); }
54630           catch (CImgException&) {
54631             is_saved = false;
54632           }
54633         }
54634       }
54635       cimg::exception_mode(omode);
54636       if (!is_saved)
54637         throw CImgIOException(_cimg_instance
54638                               "save_other(): Failed to save file '%s'. Format is not natively supported, "
54639                               "and no external commands succeeded.",
54640                               cimg_instance,
54641                               filename);
54642       return *this;
54643     }
54644 
54645     //! Serialize a CImg<T> instance into a raw CImg<unsigned char> buffer.
54646     /**
54647        \param is_compressed tells if zlib compression must be used for serialization
54648        (this requires 'cimg_use_zlib' been enabled).
54649     **/
54650     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
54651       return CImgList<T>(*this,true).get_serialize(is_compressed);
54652     }
54653 
54654     // [internal] Return a 40x38 color logo of a 'danger' item.
54655     static CImg<T> _logo40x38() {
54656       CImg<T> res(40,38,1,3);
54657       const unsigned char *ptrs = cimg::logo40x38;
54658       T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
54659       for (ulongT off = 0; off<(ulongT)res._width*res._height;) {
54660         const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
54661         for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
54662       }
54663       return res;
54664     }
54665 
54666     //@}
54667   };
54668 
54669   /*
54670    #-----------------------------------------
54671    #
54672    #
54673    #
54674    # Definition of the CImgList<T> structure
54675    #
54676    #
54677    #
54678    #------------------------------------------
54679    */
54680   //! Represent a list of images CImg<T>.
54681   template<typename T>
54682   struct CImgList {
54683     unsigned int _width, _allocated_width;
54684     CImg<T> *_data;
54685 
54686     //! Simple iterator type, to loop through each image of a list.
54687     /**
54688        \note
54689        - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>.
54690        - You may use it like this:
54691        \code
54692        CImgList<> list;   // Assuming this image list is not empty.
54693        for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x');
54694        \endcode
54695        - Using the loop macro \c cimglist_for is another (more concise) alternative:
54696        \code
54697        cimglist_for(list,l) list[l].mirror('x');
54698        \endcode
54699     **/
54700     typedef CImg<T>* iterator;
54701 
54702     //! Simple const iterator type, to loop through each image of a \c const list instance.
54703     /**
54704        \note
54705        - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>.
54706        - Similar to CImgList<T>::iterator, but for constant list instances.
54707     **/
54708     typedef const CImg<T>* const_iterator;
54709 
54710     //! Pixel value type.
54711     /**
54712        Refer to the pixels value type of the images in the list.
54713        \note
54714        - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T.
54715          It is then similar to CImg<T>::value_type.
54716        - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
54717          compatibility with STL naming conventions.
54718     **/
54719     typedef T value_type;
54720 
54721     // Define common types related to template type T.
54722     typedef typename cimg::superset<T,bool>::type Tbool;
54723     typedef typename cimg::superset<T,unsigned char>::type Tuchar;
54724     typedef typename cimg::superset<T,char>::type Tchar;
54725     typedef typename cimg::superset<T,unsigned short>::type Tushort;
54726     typedef typename cimg::superset<T,short>::type Tshort;
54727     typedef typename cimg::superset<T,unsigned int>::type Tuint;
54728     typedef typename cimg::superset<T,int>::type Tint;
54729     typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
54730     typedef typename cimg::superset<T,cimg_long>::type Tlong;
54731     typedef typename cimg::superset<T,float>::type Tfloat;
54732     typedef typename cimg::superset<T,double>::type Tdouble;
54733     typedef typename cimg::last<T,bool>::type boolT;
54734     typedef typename cimg::last<T,unsigned char>::type ucharT;
54735     typedef typename cimg::last<T,char>::type charT;
54736     typedef typename cimg::last<T,unsigned short>::type ushortT;
54737     typedef typename cimg::last<T,short>::type shortT;
54738     typedef typename cimg::last<T,unsigned int>::type uintT;
54739     typedef typename cimg::last<T,int>::type intT;
54740     typedef typename cimg::last<T,cimg_ulong>::type ulongT;
54741     typedef typename cimg::last<T,cimg_long>::type longT;
54742     typedef typename cimg::last<T,cimg_uint64>::type uint64T;
54743     typedef typename cimg::last<T,cimg_int64>::type int64T;
54744     typedef typename cimg::last<T,float>::type floatT;
54745     typedef typename cimg::last<T,double>::type doubleT;
54746 
54747     //@}
54748     //---------------------------
54749     //
54750     //! \name Plugins
54751     //@{
54752     //---------------------------
54753 #ifdef cimglist_plugin
54754 #include cimglist_plugin
54755 #endif
54756 #ifdef cimglist_plugin1
54757 #include cimglist_plugin1
54758 #endif
54759 #ifdef cimglist_plugin2
54760 #include cimglist_plugin2
54761 #endif
54762 #ifdef cimglist_plugin3
54763 #include cimglist_plugin3
54764 #endif
54765 #ifdef cimglist_plugin4
54766 #include cimglist_plugin4
54767 #endif
54768 #ifdef cimglist_plugin5
54769 #include cimglist_plugin5
54770 #endif
54771 #ifdef cimglist_plugin6
54772 #include cimglist_plugin6
54773 #endif
54774 #ifdef cimglist_plugin7
54775 #include cimglist_plugin7
54776 #endif
54777 #ifdef cimglist_plugin8
54778 #include cimglist_plugin8
54779 #endif
54780 
54781     //@}
54782     //--------------------------------------------------------
54783     //
54784     //! \name Constructors / Destructor / Instance Management
54785     //@{
54786     //--------------------------------------------------------
54787 
54788     //! Destructor.
54789     /**
54790        Destroy current list instance.
54791        \note
54792        - Any allocated buffer is deallocated.
54793        - Destroying an empty list does nothing actually.
54794      **/
54795     ~CImgList() {
54796       delete[] _data;
54797     }
54798 
54799     //! Default constructor.
54800     /**
54801        Construct a new empty list instance.
54802        \note
54803        - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its
54804          image buffer pointer data().
54805        - An empty list may be reassigned afterwards, with the family of the assign() methods.
54806          In all cases, the type of pixels stays \c T.
54807      **/
54808     CImgList():
54809       _width(0),_allocated_width(0),_data(0) {}
54810 
54811     //! Construct list containing empty images.
54812     /**
54813        \param n Number of empty images.
54814        \note Useful when you know by advance the number of images you want to manage, as
54815        it will allocate the right amount of memory for the list, without needs for reallocation
54816        (that may occur when starting from an empty list and inserting several images in it).
54817     **/
54818     explicit CImgList(const unsigned int n):_width(n) {
54819       if (n) _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
54820       else { _allocated_width = 0; _data = 0; }
54821     }
54822 
54823     //! Construct list containing images of specified size.
54824     /**
54825        \param n Number of images.
54826        \param width Width of images.
54827        \param height Height of images.
54828        \param depth Depth of images.
54829        \param spectrum Number of channels of images.
54830        \note Pixel values are not initialized and may probably contain garbage.
54831     **/
54832     CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
54833              const unsigned int depth=1, const unsigned int spectrum=1):
54834       _width(0),_allocated_width(0),_data(0) {
54835       assign(n);
54836       cimglist_apply(*this,assign)(width,height,depth,spectrum);
54837     }
54838 
54839     //! Construct list containing images of specified size, and initialize pixel values.
54840     /**
54841        \param n Number of images.
54842        \param width Width of images.
54843        \param height Height of images.
54844        \param depth Depth of images.
54845        \param spectrum Number of channels of images.
54846        \param val Initialization value for images pixels.
54847     **/
54848     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
54849              const unsigned int depth, const unsigned int spectrum, const T& val):
54850       _width(0),_allocated_width(0),_data(0) {
54851       assign(n);
54852       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
54853     }
54854 
54855     //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers.
54856     /**
54857        \param n Number of images.
54858        \param width Width of images.
54859        \param height Height of images.
54860        \param depth Depth of images.
54861        \param spectrum Number of channels of images.
54862        \param val0 First value of the initializing integers sequence.
54863        \param val1 Second value of the initializing integers sequence.
54864        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
54865          or you will probably segfault.
54866     **/
54867     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
54868              const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
54869       _width(0),_allocated_width(0),_data(0) {
54870 #define _CImgList_stdarg(t) { \
54871 	assign(n,width,height,depth,spectrum); \
54872 	const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \
54873 	T *ptrd = _data->_data; \
54874 	va_list ap; \
54875 	va_start(ap,val1); \
54876 	for (ulongT l = 0, s = 0, i = 0; i<nsiz; ++i) { \
54877 	  *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
54878 	  if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
54879 	} \
54880 	va_end(ap); \
54881       }
54882       _CImgList_stdarg(int);
54883     }
54884 
54885     //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles.
54886     /**
54887        \param n Number of images.
54888        \param width Width of images.
54889        \param height Height of images.
54890        \param depth Depth of images.
54891        \param spectrum Number of channels of images.
54892        \param val0 First value of the initializing doubles sequence.
54893        \param val1 Second value of the initializing doubles sequence.
54894        \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
54895          or you will probably segfault.
54896     **/
54897     CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
54898              const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
54899       _width(0),_allocated_width(0),_data(0) {
54900       _CImgList_stdarg(double);
54901     }
54902 
54903     //! Construct list containing copies of an input image.
54904     /**
54905        \param n Number of images.
54906        \param img Input image to copy in the constructed list.
54907        \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img.
54908     **/
54909     template<typename t>
54910     CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
54911       _width(0),_allocated_width(0),_data(0) {
54912       assign(n);
54913       cimglist_apply(*this,assign)(img,is_shared);
54914     }
54915 
54916     //! Construct list from one image.
54917     /**
54918        \param img Input image to copy in the constructed list.
54919        \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img.
54920      **/
54921     template<typename t>
54922     explicit CImgList(const CImg<t>& img, const bool is_shared=false):
54923       _width(0),_allocated_width(0),_data(0) {
54924       assign(1);
54925       _data[0].assign(img,is_shared);
54926     }
54927 
54928     //! Construct list from two images.
54929     /**
54930        \param img1 First input image to copy in the constructed list.
54931        \param img2 Second input image to copy in the constructed list.
54932        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54933      **/
54934     template<typename t1, typename t2>
54935     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
54936       _width(0),_allocated_width(0),_data(0) {
54937       assign(2);
54938       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
54939     }
54940 
54941     //! Construct list from three images.
54942     /**
54943        \param img1 First input image to copy in the constructed list.
54944        \param img2 Second input image to copy in the constructed list.
54945        \param img3 Third input image to copy in the constructed list.
54946        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54947     **/
54948     template<typename t1, typename t2, typename t3>
54949     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
54950       _width(0),_allocated_width(0),_data(0) {
54951       assign(3);
54952       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
54953     }
54954 
54955     //! Construct list from four images.
54956     /**
54957        \param img1 First input image to copy in the constructed list.
54958        \param img2 Second input image to copy in the constructed list.
54959        \param img3 Third input image to copy in the constructed list.
54960        \param img4 Fourth input image to copy in the constructed list.
54961        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54962     **/
54963     template<typename t1, typename t2, typename t3, typename t4>
54964     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
54965              const bool is_shared=false):
54966       _width(0),_allocated_width(0),_data(0) {
54967       assign(4);
54968       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
54969       _data[3].assign(img4,is_shared);
54970     }
54971 
54972     //! Construct list from five images.
54973     /**
54974        \param img1 First input image to copy in the constructed list.
54975        \param img2 Second input image to copy in the constructed list.
54976        \param img3 Third input image to copy in the constructed list.
54977        \param img4 Fourth input image to copy in the constructed list.
54978        \param img5 Fifth input image to copy in the constructed list.
54979        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54980     **/
54981     template<typename t1, typename t2, typename t3, typename t4, typename t5>
54982     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
54983              const CImg<t5>& img5, const bool is_shared=false):
54984       _width(0),_allocated_width(0),_data(0) {
54985       assign(5);
54986       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
54987       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
54988     }
54989 
54990     //! Construct list from six images.
54991     /**
54992        \param img1 First input image to copy in the constructed list.
54993        \param img2 Second input image to copy in the constructed list.
54994        \param img3 Third input image to copy in the constructed list.
54995        \param img4 Fourth input image to copy in the constructed list.
54996        \param img5 Fifth input image to copy in the constructed list.
54997        \param img6 Sixth input image to copy in the constructed list.
54998        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
54999     **/
55000     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
55001     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55002              const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
55003       _width(0),_allocated_width(0),_data(0) {
55004       assign(6);
55005       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55006       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
55007     }
55008 
55009     //! Construct list from seven images.
55010     /**
55011        \param img1 First input image to copy in the constructed list.
55012        \param img2 Second input image to copy in the constructed list.
55013        \param img3 Third input image to copy in the constructed list.
55014        \param img4 Fourth input image to copy in the constructed list.
55015        \param img5 Fifth input image to copy in the constructed list.
55016        \param img6 Sixth input image to copy in the constructed list.
55017        \param img7 Seventh input image to copy in the constructed list.
55018        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
55019     **/
55020     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
55021     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55022              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
55023       _width(0),_allocated_width(0),_data(0) {
55024       assign(7);
55025       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55026       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
55027       _data[6].assign(img7,is_shared);
55028     }
55029 
55030     //! Construct list from eight images.
55031     /**
55032        \param img1 First input image to copy in the constructed list.
55033        \param img2 Second input image to copy in the constructed list.
55034        \param img3 Third input image to copy in the constructed list.
55035        \param img4 Fourth input image to copy in the constructed list.
55036        \param img5 Fifth input image to copy in the constructed list.
55037        \param img6 Sixth input image to copy in the constructed list.
55038        \param img7 Seventh input image to copy in the constructed list.
55039        \param img8 Eighth input image to copy in the constructed list.
55040        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
55041     **/
55042     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
55043     CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55044              const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
55045              const bool is_shared=false):
55046       _width(0),_allocated_width(0),_data(0) {
55047       assign(8);
55048       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55049       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
55050       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
55051     }
55052 
55053     //! Construct list copy.
55054     /**
55055        \param list Input list to copy.
55056        \note The shared state of each element of the constructed list is kept the same as in \c list.
55057     **/
55058     template<typename t>
55059     CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
55060       assign(list._width);
55061       cimglist_for(*this,l) _data[l].assign(list[l],false);
55062     }
55063 
55064     //! Construct list copy \specialization.
55065     CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
55066       assign(list._width);
55067       cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
55068     }
55069 
55070     //! Construct list copy, and force the shared state of the list elements.
55071     /**
55072        \param list Input list to copy.
55073        \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
55074     **/
55075     template<typename t>
55076     CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
55077       assign(list._width);
55078       cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
55079     }
55080 
55081     //! Construct list by reading the content of a file.
55082     /**
55083        \param filename Filename, as a C-string.
55084     **/
55085     explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
55086       assign(filename);
55087     }
55088 
55089     //! Construct list from the content of a display window.
55090     /**
55091        \param disp Display window to get content from.
55092        \note Constructed list contains a single image only.
55093     **/
55094     explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
55095       assign(disp);
55096     }
55097 
55098     //! Return a list with elements being shared copies of images in the list instance.
55099     /**
55100       \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>.
55101     **/
55102     CImgList<T> get_shared() {
55103       CImgList<T> res(_width);
55104       cimglist_for(*this,l) res[l].assign(_data[l],true);
55105       return res;
55106     }
55107 
55108     //! Return a list with elements being shared copies of images in the list instance \const.
55109     const CImgList<T> get_shared() const {
55110       CImgList<T> res(_width);
55111       cimglist_for(*this,l) res[l].assign(_data[l],true);
55112       return res;
55113     }
55114 
55115     //! Destructor \inplace.
55116     /**
55117        \see CImgList().
55118     **/
55119     CImgList<T>& assign() {
55120       delete[] _data;
55121       _width = _allocated_width = 0;
55122       _data = 0;
55123       return *this;
55124     }
55125 
55126     //! Destructor \inplace.
55127     /**
55128        Equivalent to assign().
55129        \note Only here for compatibility with STL naming conventions.
55130     **/
55131     CImgList<T>& clear() {
55132       return assign();
55133     }
55134 
55135     //! Construct list containing empty images \inplace.
55136     /**
55137        \see CImgList(unsigned int).
55138     **/
55139     CImgList<T>& assign(const unsigned int n) {
55140       if (!n) return assign();
55141       if (_allocated_width<n || _allocated_width>(n<<2)) {
55142         delete[] _data;
55143         _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
55144       }
55145       _width = n;
55146       return *this;
55147     }
55148 
55149     //! Construct list containing images of specified size \inplace.
55150     /**
55151        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int).
55152     **/
55153     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
55154                         const unsigned int depth=1, const unsigned int spectrum=1) {
55155       assign(n);
55156       cimglist_apply(*this,assign)(width,height,depth,spectrum);
55157       return *this;
55158     }
55159 
55160     //! Construct list containing images of specified size, and initialize pixel values \inplace.
55161     /**
55162        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T).
55163     **/
55164     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
55165                         const unsigned int depth, const unsigned int spectrum, const T& val) {
55166       assign(n);
55167       cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
55168       return *this;
55169     }
55170 
55171     //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace.
55172     /**
55173        \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...).
55174     **/
55175     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
55176                         const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
55177       _CImgList_stdarg(int);
55178       return *this;
55179     }
55180 
55181     //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace.
55182     /**
55183        \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...).
55184     **/
55185     CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
55186                         const unsigned int depth, const unsigned int spectrum,
55187                         const double val0, const double val1, ...) {
55188       _CImgList_stdarg(double);
55189       return *this;
55190     }
55191 
55192     //! Construct list containing copies of an input image \inplace.
55193     /**
55194        \see CImgList(unsigned int, const CImg<t>&, bool).
55195     **/
55196     template<typename t>
55197     CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
55198       assign(n);
55199       cimglist_apply(*this,assign)(img,is_shared);
55200       return *this;
55201     }
55202 
55203     //! Construct list from one image \inplace.
55204     /**
55205        \see CImgList(const CImg<t>&, bool).
55206     **/
55207     template<typename t>
55208     CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
55209       assign(1);
55210       _data[0].assign(img,is_shared);
55211       return *this;
55212     }
55213 
55214     //! Construct list from two images \inplace.
55215     /**
55216        \see CImgList(const CImg<t>&, const CImg<t>&, bool).
55217     **/
55218     template<typename t1, typename t2>
55219     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
55220       assign(2);
55221       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
55222       return *this;
55223     }
55224 
55225     //! Construct list from three images \inplace.
55226     /**
55227        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
55228     **/
55229     template<typename t1, typename t2, typename t3>
55230     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
55231       assign(3);
55232       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55233       return *this;
55234     }
55235 
55236     //! Construct list from four images \inplace.
55237     /**
55238        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
55239     **/
55240     template<typename t1, typename t2, typename t3, typename t4>
55241     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55242                         const bool is_shared=false) {
55243       assign(4);
55244       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55245       _data[3].assign(img4,is_shared);
55246       return *this;
55247     }
55248 
55249     //! Construct list from five images \inplace.
55250     /**
55251        \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
55252     **/
55253     template<typename t1, typename t2, typename t3, typename t4, typename t5>
55254     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55255                         const CImg<t5>& img5, const bool is_shared=false) {
55256       assign(5);
55257       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55258       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
55259       return *this;
55260     }
55261 
55262     //! Construct list from six images \inplace.
55263     /**
55264        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, bool).
55265     **/
55266     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
55267     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55268                         const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
55269       assign(6);
55270       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55271       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
55272       return *this;
55273     }
55274 
55275     //! Construct list from seven images \inplace.
55276     /**
55277        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
55278        const CImg<t>&, bool).
55279     **/
55280     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
55281     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55282                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
55283       assign(7);
55284       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55285       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
55286       _data[6].assign(img7,is_shared);
55287       return *this;
55288     }
55289 
55290     //! Construct list from eight images \inplace.
55291     /**
55292        \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
55293        const CImg<t>&, const CImg<t>&, bool).
55294     **/
55295     template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
55296     CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
55297                         const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
55298                         const bool is_shared=false) {
55299       assign(8);
55300       _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
55301       _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
55302       _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
55303       return *this;
55304     }
55305 
55306     //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace.
55307     /**
55308       \see CImgList(const CImgList<t>&, bool is_shared).
55309     **/
55310     template<typename t>
55311     CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
55312       cimg::unused(is_shared);
55313       assign(list._width);
55314       cimglist_for(*this,l) _data[l].assign(list[l],false);
55315       return *this;
55316     }
55317 
55318     //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization.
55319     CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
55320       if (this==&list) return *this;
55321       CImgList<T> res(list._width);
55322       cimglist_for(res,l) res[l].assign(list[l],is_shared);
55323       return res.move_to(*this);
55324     }
55325 
55326     //! Construct list by reading the content of a file \inplace.
55327     /**
55328       \see CImgList(const char *const).
55329     **/
55330     CImgList<T>& assign(const char *const filename) {
55331       return load(filename);
55332     }
55333 
55334     //! Construct list from the content of a display window \inplace.
55335     /**
55336       \see CImgList(const CImgDisplay&).
55337     **/
55338     CImgList<T>& assign(const CImgDisplay &disp) {
55339       return assign(CImg<T>(disp));
55340     }
55341 
55342     //! Transfer the content of the list instance to another list.
55343     /**
55344        \param list Destination list.
55345        \note When returning, the current list instance is empty and the initial content of \c list is destroyed.
55346     **/
55347     template<typename t>
55348     CImgList<t>& move_to(CImgList<t>& list) {
55349       list.assign(_width);
55350       bool is_one_shared_element = false;
55351       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
55352       if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
55353       else cimglist_for(*this,l) _data[l].move_to(list[l]);
55354       assign();
55355       return list;
55356     }
55357 
55358     //! Transfer the content of the list instance at a specified position in another list.
55359     /**
55360        \param list Destination list.
55361        \param pos Index of the insertion in the list.
55362        \note When returning, the list instance is empty and the initial content of \c list is preserved
55363        (only images indexes may be modified).
55364      **/
55365     template<typename t>
55366     CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
55367       if (is_empty()) return list;
55368       const unsigned int npos = pos>list._width?list._width:pos;
55369       list.insert(_width,npos);
55370       bool is_one_shared_element = false;
55371       cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
55372       if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]);
55373       else cimglist_for(*this,l) _data[l].move_to(list[npos + l]);
55374       assign();
55375       return list;
55376     }
55377 
55378     //! Swap all fields between two list instances.
55379     /**
55380        \param list List to swap fields with.
55381        \note Can be used to exchange the content of two lists in a fast way.
55382     **/
55383     CImgList<T>& swap(CImgList<T>& list) {
55384       cimg::swap(_width,list._width,_allocated_width,list._allocated_width);
55385       cimg::swap(_data,list._data);
55386       return list;
55387     }
55388 
55389     //! Return a reference to an empty list.
55390     /**
55391       \note Can be used to define default values in a function taking a CImgList<T> as an argument.
55392       \code
55393       void f(const CImgList<char>& list=CImgList<char>::empty());
55394       \endcode
55395     **/
55396     static CImgList<T>& empty() {
55397       static CImgList<T> _empty;
55398       return _empty.assign();
55399     }
55400 
55401     //! Return a reference to an empty list \const.
55402     static const CImgList<T>& const_empty() {
55403       static const CImgList<T> _empty;
55404       return _empty;
55405     }
55406 
55407     //@}
55408     //------------------------------------------
55409     //
55410     //! \name Overloaded Operators
55411     //@{
55412     //------------------------------------------
55413 
55414     //! Return a reference to one image element of the list.
55415     /**
55416        \param pos Indice of the image element.
55417     **/
55418     CImg<T>& operator()(const unsigned int pos) {
55419 #if cimg_verbosity>=3
55420       if (pos>=_width) {
55421         cimg::warn(_cimglist_instance
55422                    "operator(): Invalid image request, at position [%u].",
55423                    cimglist_instance,
55424                    pos);
55425         return *_data;
55426       }
55427 #endif
55428       return _data[pos];
55429     }
55430 
55431     //! Return a reference to one image of the list.
55432     /**
55433        \param pos Indice of the image element.
55434     **/
55435     const CImg<T>& operator()(const unsigned int pos) const {
55436       return const_cast<CImgList<T>*>(this)->operator()(pos);
55437     }
55438 
55439     //! Return a reference to one pixel value of one image of the list.
55440     /**
55441        \param pos Indice of the image element.
55442        \param x X-coordinate of the pixel value.
55443        \param y Y-coordinate of the pixel value.
55444        \param z Z-coordinate of the pixel value.
55445        \param c C-coordinate of the pixel value.
55446        \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>.
55447     **/
55448     T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
55449                   const unsigned int z=0, const unsigned int c=0) {
55450       return (*this)[pos](x,y,z,c);
55451     }
55452 
55453     //! Return a reference to one pixel value of one image of the list \const.
55454     const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
55455                         const unsigned int z=0, const unsigned int c=0) const {
55456       return (*this)[pos](x,y,z,c);
55457     }
55458 
55459     //! Return pointer to the first image of the list.
55460     /**
55461        \note Images in a list are stored as a buffer of \c CImg<T>.
55462     **/
55463     operator CImg<T>*() {
55464       return _data;
55465     }
55466 
55467     //! Return pointer to the first image of the list \const.
55468     operator const CImg<T>*() const {
55469       return _data;
55470     }
55471 
55472     //! Construct list from one image \inplace.
55473     /**
55474         \param img Input image to copy in the constructed list.
55475         \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>.
55476     **/
55477     template<typename t>
55478     CImgList<T>& operator=(const CImg<t>& img) {
55479       return assign(img);
55480     }
55481 
55482     //! Construct list from another list.
55483     /**
55484        \param list Input list to copy.
55485        \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>.
55486     **/
55487     template<typename t>
55488     CImgList<T>& operator=(const CImgList<t>& list) {
55489       return assign(list);
55490     }
55491 
55492     //! Construct list from another list \specialization.
55493     CImgList<T>& operator=(const CImgList<T>& list) {
55494       return assign(list);
55495     }
55496 
55497     //! Construct list by reading the content of a file \inplace.
55498     /**
55499        \see CImgList(const char *const).
55500     **/
55501     CImgList<T>& operator=(const char *const filename) {
55502       return assign(filename);
55503     }
55504 
55505     //! Construct list from the content of a display window \inplace.
55506     /**
55507         \see CImgList(const CImgDisplay&).
55508     **/
55509     CImgList<T>& operator=(const CImgDisplay& disp) {
55510       return assign(disp);
55511     }
55512 
55513     //! Return a non-shared copy of a list.
55514     /**
55515         \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>.
55516           It forces the copy to have non-shared elements.
55517     **/
55518     CImgList<T> operator+() const {
55519       return CImgList<T>(*this,false);
55520     }
55521 
55522     //! Return a copy of the list instance, where image \c img has been inserted at the end.
55523     /**
55524        \param img Image inserted at the end of the instance copy.
55525        \note Define a convenient way to create temporary lists of images, as in the following code:
55526        \code
55527        (img1,img2,img3,img4).display("My four images");
55528        \endcode
55529     **/
55530     template<typename t>
55531     CImgList<T>& operator,(const CImg<t>& img) {
55532       return insert(img);
55533     }
55534 
55535     //! Return a copy of the list instance, where image \c img has been inserted at the end \const.
55536     template<typename t>
55537     CImgList<T> operator,(const CImg<t>& img) const {
55538       return (+*this).insert(img);
55539     }
55540 
55541     //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end.
55542     /**
55543        \param list List inserted at the end of the instance copy.
55544     **/
55545     template<typename t>
55546     CImgList<T>& operator,(const CImgList<t>& list) {
55547       return insert(list);
55548     }
55549 
55550     //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const.
55551     template<typename t>
55552     CImgList<T>& operator,(const CImgList<t>& list) const {
55553       return (+*this).insert(list);
55554     }
55555 
55556     //! Return image corresponding to the appending of all images of the instance list along specified axis.
55557     /**
55558       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
55559       \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>.
55560     **/
55561     CImg<T> operator>(const char axis) const {
55562       return get_append(axis,0);
55563     }
55564 
55565     //! Return list corresponding to the splitting of all images of the instance list along specified axis.
55566     /**
55567       \param axis Axis used for image splitting.
55568       \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>.
55569     **/
55570     CImgList<T> operator<(const char axis) const {
55571       return get_split(axis);
55572     }
55573 
55574     //@}
55575     //-------------------------------------
55576     //
55577     //! \name Instance Characteristics
55578     //@{
55579     //-------------------------------------
55580 
55581     //! Return the type of image pixel values as a C string.
55582     /**
55583        Return a \c char* string containing the usual type name of the image pixel values
55584        (i.e. a stringified version of the template parameter \c T).
55585        \note
55586        - The returned string may contain spaces (as in \c "unsigned char").
55587        - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
55588     **/
55589     static const char* pixel_type() {
55590       return cimg::type<T>::string();
55591     }
55592 
55593     //! Return the size of the list, i.e. the number of images contained in it.
55594     /**
55595       \note Similar to size() but returns result as a (signed) integer.
55596     **/
55597     int width() const {
55598       return (int)_width;
55599     }
55600 
55601     //! Return the size of the list, i.e. the number of images contained in it.
55602     /**
55603       \note Similar to width() but returns result as an unsigned integer.
55604     **/
55605     unsigned int size() const {
55606       return _width;
55607     }
55608 
55609     //! Return pointer to the first image of the list.
55610     /**
55611        \note Images in a list are stored as a buffer of \c CImg<T>.
55612     **/
55613     CImg<T> *data() {
55614       return _data;
55615     }
55616 
55617     //! Return pointer to the first image of the list \const.
55618     const CImg<T> *data() const {
55619       return _data;
55620     }
55621 
55622     //! Return pointer to the pos-th image of the list.
55623     /**
55624        \param pos Indice of the image element to access.
55625        \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>.
55626     **/
55627 #if cimg_verbosity>=3
55628     CImg<T> *data(const unsigned int pos) {
55629       if (pos>=size())
55630         cimg::warn(_cimglist_instance
55631                    "data(): Invalid pointer request, at position [%u].",
55632                    cimglist_instance,
55633                    pos);
55634       return _data + pos;
55635     }
55636 
55637     const CImg<T> *data(const unsigned int l) const {
55638       return const_cast<CImgList<T>*>(this)->data(l);
55639     }
55640 #else
55641     CImg<T> *data(const unsigned int l) {
55642       return _data + l;
55643     }
55644 
55645     //! Return pointer to the pos-th image of the list \const.
55646     const CImg<T> *data(const unsigned int l) const {
55647       return _data + l;
55648     }
55649 #endif
55650 
55651     //! Return iterator to the first image of the list.
55652     /**
55653     **/
55654     iterator begin() {
55655       return _data;
55656     }
55657 
55658     //! Return iterator to the first image of the list \const.
55659     const_iterator begin() const {
55660       return _data;
55661     }
55662 
55663     //! Return iterator to one position after the last image of the list.
55664     /**
55665     **/
55666     iterator end() {
55667       return _data + _width;
55668     }
55669 
55670     //! Return iterator to one position after the last image of the list \const.
55671     const_iterator end() const {
55672       return _data + _width;
55673     }
55674 
55675     //! Return reference to the first image of the list.
55676     /**
55677     **/
55678     CImg<T>& front() {
55679       return *_data;
55680     }
55681 
55682     //! Return reference to the first image of the list \const.
55683     const CImg<T>& front() const {
55684       return *_data;
55685     }
55686 
55687     //! Return a reference to the last image of the list.
55688     /**
55689     **/
55690     const CImg<T>& back() const {
55691       return *(_data + _width - 1);
55692     }
55693 
55694     //! Return a reference to the last image of the list \const.
55695     CImg<T>& back() {
55696       return *(_data + _width - 1);
55697     }
55698 
55699     //! Return pos-th image of the list.
55700     /**
55701        \param pos Indice of the image element to access.
55702     **/
55703     CImg<T>& at(const int pos) {
55704       if (is_empty())
55705         throw CImgInstanceException(_cimglist_instance
55706                                     "at(): Empty instance.",
55707                                     cimglist_instance);
55708 
55709       return _data[cimg::cut(pos,0,width() - 1)];
55710     }
55711 
55712     //! Access to pixel value with Dirichlet boundary conditions.
55713     /**
55714        \param pos Indice of the image element to access.
55715        \param x X-coordinate of the pixel value.
55716        \param y Y-coordinate of the pixel value.
55717        \param z Z-coordinate of the pixel value.
55718        \param c C-coordinate of the pixel value.
55719        \param out_value Default value returned if \c offset is outside image bounds.
55720        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
55721     **/
55722     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55723       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
55724     }
55725 
55726     //! Access to pixel value with Dirichlet boundary conditions \const.
55727     T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55728       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
55729     }
55730 
55731     //! Access to pixel value with Neumann boundary conditions.
55732     /**
55733        \param pos Indice of the image element to access.
55734        \param x X-coordinate of the pixel value.
55735        \param y Y-coordinate of the pixel value.
55736        \param z Z-coordinate of the pixel value.
55737        \param c C-coordinate of the pixel value.
55738        \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
55739     **/
55740     T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
55741       if (is_empty())
55742         throw CImgInstanceException(_cimglist_instance
55743                                     "atNXYZC(): Empty instance.",
55744                                     cimglist_instance);
55745 
55746       return _atNXYZC(pos,x,y,z,c);
55747     }
55748 
55749     //! Access to pixel value with Neumann boundary conditions \const.
55750     T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
55751       if (is_empty())
55752         throw CImgInstanceException(_cimglist_instance
55753                                     "atNXYZC(): Empty instance.",
55754                                     cimglist_instance);
55755 
55756       return _atNXYZC(pos,x,y,z,c);
55757     }
55758 
55759     T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
55760       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
55761     }
55762 
55763     T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
55764       return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
55765     }
55766 
55767     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z).
55768     /**
55769        \param pos Indice of the image element to access.
55770        \param x X-coordinate of the pixel value.
55771        \param y Y-coordinate of the pixel value.
55772        \param z Z-coordinate of the pixel value.
55773        \param c C-coordinate of the pixel value.
55774        \param out_value Default value returned if \c offset is outside image bounds.
55775        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55776     **/
55777     T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55778       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
55779     }
55780 
55781     //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const.
55782     T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55783       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
55784     }
55785 
55786     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z).
55787     /**
55788        \param pos Indice of the image element to access.
55789        \param x X-coordinate of the pixel value.
55790        \param y Y-coordinate of the pixel value.
55791        \param z Z-coordinate of the pixel value.
55792        \param c C-coordinate of the pixel value.
55793        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55794     **/
55795    T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
55796       if (is_empty())
55797         throw CImgInstanceException(_cimglist_instance
55798                                     "atNXYZ(): Empty instance.",
55799                                     cimglist_instance);
55800 
55801       return _atNXYZ(pos,x,y,z,c);
55802     }
55803 
55804     //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const.
55805     T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
55806       if (is_empty())
55807         throw CImgInstanceException(_cimglist_instance
55808                                     "atNXYZ(): Empty instance.",
55809                                     cimglist_instance);
55810 
55811       return _atNXYZ(pos,x,y,z,c);
55812     }
55813 
55814     T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
55815       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
55816     }
55817 
55818     T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
55819       return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
55820     }
55821 
55822     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
55823     /**
55824        \param pos Indice of the image element to access.
55825        \param x X-coordinate of the pixel value.
55826        \param y Y-coordinate of the pixel value.
55827        \param z Z-coordinate of the pixel value.
55828        \param c C-coordinate of the pixel value.
55829        \param out_value Default value returned if \c offset is outside image bounds.
55830        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55831     **/
55832     T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55833       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
55834     }
55835 
55836     //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
55837     T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55838       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXY(x,y,z,c,out_value);
55839     }
55840 
55841     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
55842     /**
55843        \param pos Indice of the image element to access.
55844        \param x X-coordinate of the pixel value.
55845        \param y Y-coordinate of the pixel value.
55846        \param z Z-coordinate of the pixel value.
55847        \param c C-coordinate of the pixel value.
55848        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55849     **/
55850     T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
55851       if (is_empty())
55852         throw CImgInstanceException(_cimglist_instance
55853                                     "atNXY(): Empty instance.",
55854                                     cimglist_instance);
55855 
55856       return _atNXY(pos,x,y,z,c);
55857     }
55858 
55859     //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
55860     T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
55861       if (is_empty())
55862         throw CImgInstanceException(_cimglist_instance
55863                                     "atNXY(): Empty instance.",
55864                                     cimglist_instance);
55865 
55866       return _atNXY(pos,x,y,z,c);
55867     }
55868 
55869     T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
55870       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
55871     }
55872 
55873     T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
55874       return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
55875     }
55876 
55877     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x).
55878     /**
55879        \param pos Indice of the image element to access.
55880        \param x X-coordinate of the pixel value.
55881        \param y Y-coordinate of the pixel value.
55882        \param z Z-coordinate of the pixel value.
55883        \param c C-coordinate of the pixel value.
55884        \param out_value Default value returned if \c offset is outside image bounds.
55885        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55886     **/
55887     T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55888       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
55889     }
55890 
55891     //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const.
55892     T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55893       return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atX(x,y,z,c,out_value);
55894     }
55895 
55896     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x).
55897     /**
55898        \param pos Indice of the image element to access.
55899        \param x X-coordinate of the pixel value.
55900        \param y Y-coordinate of the pixel value.
55901        \param z Z-coordinate of the pixel value.
55902        \param c C-coordinate of the pixel value.
55903        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55904     **/
55905     T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
55906       if (is_empty())
55907         throw CImgInstanceException(_cimglist_instance
55908                                     "atNX(): Empty instance.",
55909                                     cimglist_instance);
55910 
55911       return _atNX(pos,x,y,z,c);
55912     }
55913 
55914     //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const.
55915     T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
55916       if (is_empty())
55917         throw CImgInstanceException(_cimglist_instance
55918                                     "atNX(): Empty instance.",
55919                                     cimglist_instance);
55920 
55921       return _atNX(pos,x,y,z,c);
55922     }
55923 
55924     T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
55925       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
55926     }
55927 
55928     T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
55929       return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
55930     }
55931 
55932     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos).
55933     /**
55934        \param pos Indice of the image element to access.
55935        \param x X-coordinate of the pixel value.
55936        \param y Y-coordinate of the pixel value.
55937        \param z Z-coordinate of the pixel value.
55938        \param c C-coordinate of the pixel value.
55939        \param out_value Default value returned if \c offset is outside image bounds.
55940        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55941     **/
55942     T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
55943       return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
55944     }
55945 
55946     //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const.
55947     T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
55948       return (pos<0 || pos>=(int)_width)?out_value:(*this)(pos,x,y,z,c);
55949     }
55950 
55951     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos).
55952     /**
55953        \param pos Indice of the image element to access.
55954        \param x X-coordinate of the pixel value.
55955        \param y Y-coordinate of the pixel value.
55956        \param z Z-coordinate of the pixel value.
55957        \param c C-coordinate of the pixel value.
55958        \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
55959     **/
55960     T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
55961       if (is_empty())
55962         throw CImgInstanceException(_cimglist_instance
55963                                     "atN(): Empty instance.",
55964                                     cimglist_instance);
55965       return _atN(pos,x,y,z,c);
55966     }
55967 
55968     //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const.
55969     T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
55970       if (is_empty())
55971         throw CImgInstanceException(_cimglist_instance
55972                                     "atN(): Empty instance.",
55973                                     cimglist_instance);
55974       return _atN(pos,x,y,z,c);
55975     }
55976 
55977     T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
55978       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
55979     }
55980 
55981     T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
55982       return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
55983     }
55984 
55985     //@}
55986     //-------------------------------------
55987     //
55988     //! \name Instance Checking
55989     //@{
55990     //-------------------------------------
55991 
55992     //! Return \c true if list is empty.
55993     /**
55994     **/
55995     bool is_empty() const {
55996       return (!_data || !_width);
55997     }
55998 
55999     //! Test if number of image elements is equal to specified value.
56000     /**
56001         \param size_n Number of image elements to test.
56002     **/
56003     bool is_sameN(const unsigned int size_n) const {
56004       return _width==size_n;
56005     }
56006 
56007     //! Test if number of image elements is equal between two images lists.
56008     /**
56009         \param list Input list to compare with.
56010     **/
56011     template<typename t>
56012     bool is_sameN(const CImgList<t>& list) const {
56013       return is_sameN(list._width);
56014     }
56015 
56016     // Define useful functions to check list dimensions.
56017     // (cannot be documented because macro-generated).
56018 #define _cimglist_def_is_same1(axis) \
56019     bool is_same##axis(const unsigned int val) const { \
56020       bool res = true; \
56021       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); return res; \
56022     } \
56023     bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
56024       return is_sameN(n) && is_same##axis(val); \
56025     } \
56026 
56027 #define _cimglist_def_is_same2(axis1,axis2) \
56028     bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
56029       bool res = true; \
56030       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); return res; \
56031     } \
56032     bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
56033       return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
56034     } \
56035 
56036 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
56037     bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \
56038                                       const unsigned int val3) const { \
56039       bool res = true; \
56040       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \
56041       return res; \
56042     } \
56043     bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \
56044                                        const unsigned int val2, const unsigned int val3) const { \
56045       return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
56046     } \
56047 
56048 #define _cimglist_def_is_same(axis) \
56049     template<typename t> bool is_same##axis(const CImg<t>& img) const { \
56050       bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \
56051     } \
56052     template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
56053       const unsigned int lmin = std::min(_width,list._width); \
56054       bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); return res; \
56055     } \
56056     template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
56057       return (is_sameN(n) && is_same##axis(img)); \
56058     } \
56059     template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
56060       return (is_sameN(list) && is_same##axis(list)); \
56061     }
56062 
56063     _cimglist_def_is_same(XY)
56064     _cimglist_def_is_same(XZ)
56065     _cimglist_def_is_same(XC)
56066     _cimglist_def_is_same(YZ)
56067     _cimglist_def_is_same(YC)
56068     _cimglist_def_is_same(XYZ)
56069     _cimglist_def_is_same(XYC)
56070     _cimglist_def_is_same(YZC)
56071     _cimglist_def_is_same(XYZC)
56072     _cimglist_def_is_same1(X)
56073     _cimglist_def_is_same1(Y)
56074     _cimglist_def_is_same1(Z)
56075     _cimglist_def_is_same1(C)
56076     _cimglist_def_is_same2(X,Y)
56077     _cimglist_def_is_same2(X,Z)
56078     _cimglist_def_is_same2(X,C)
56079     _cimglist_def_is_same2(Y,Z)
56080     _cimglist_def_is_same2(Y,C)
56081     _cimglist_def_is_same2(Z,C)
56082     _cimglist_def_is_same3(X,Y,Z)
56083     _cimglist_def_is_same3(X,Y,C)
56084     _cimglist_def_is_same3(X,Z,C)
56085     _cimglist_def_is_same3(Y,Z,C)
56086 
56087     //! Test if dimensions of each image of the list match specified arguments.
56088     /**
56089       \param dx Checked image width.
56090       \param dy Checked image height.
56091       \param dz Checked image depth.
56092       \param dc Checked image spectrum.
56093     **/
56094     bool is_sameXYZC(const unsigned int dx, const unsigned int dy,
56095                      const unsigned int dz, const unsigned int dc) const {
56096       bool res = true;
56097       for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
56098       return res;
56099     }
56100 
56101     //! Test if list dimensions match specified arguments.
56102     /**
56103        \param n Number of images in the list.
56104        \param dx Checked image width.
56105        \param dy Checked image height.
56106        \param dz Checked image depth.
56107        \param dc Checked image spectrum.
56108     **/
56109     bool is_sameNXYZC(const unsigned int n,
56110                       const unsigned int dx, const unsigned int dy,
56111                       const unsigned int dz, const unsigned int dc) const {
56112       return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
56113     }
56114 
56115     //! Test if list contains one particular pixel location.
56116     /**
56117        \param n Index of the image whom checked pixel value belong to.
56118        \param x X-coordinate of the checked pixel value.
56119        \param y Y-coordinate of the checked pixel value.
56120        \param z Z-coordinate of the checked pixel value.
56121        \param c C-coordinate of the checked pixel value.
56122     **/
56123     bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
56124       if (is_empty()) return false;
56125       return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
56126         z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
56127     }
56128 
56129     //! Test if list contains image with specified indice.
56130     /**
56131        \param n Index of the checked image.
56132     **/
56133     bool containsN(const int n) const {
56134       if (is_empty()) return false;
56135       return n>=0 && n<(int)_width;
56136     }
56137 
56138     //! Test if one image of the list contains the specified referenced value.
56139     /**
56140        \param pixel Reference to pixel value to test.
56141        \param[out] n Index of image containing the pixel value, if test succeeds.
56142        \param[out] x X-coordinate of the pixel value, if test succeeds.
56143        \param[out] y Y-coordinate of the pixel value, if test succeeds.
56144        \param[out] z Z-coordinate of the pixel value, if test succeeds.
56145        \param[out] c C-coordinate of the pixel value, if test succeeds.
56146        \note If true, set coordinates (n,x,y,z,c).
56147     **/
56148     template<typename t>
56149     bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
56150       if (is_empty()) return false;
56151       cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
56152       return false;
56153     }
56154 
56155     //! Test if one of the image list contains the specified referenced value.
56156     /**
56157        \param pixel Reference to pixel value to test.
56158        \param[out] n Index of image containing the pixel value, if test succeeds.
56159        \param[out] x X-coordinate of the pixel value, if test succeeds.
56160        \param[out] y Y-coordinate of the pixel value, if test succeeds.
56161        \param[out] z Z-coordinate of the pixel value, if test succeeds.
56162        \note If true, set coordinates (n,x,y,z).
56163     **/
56164     template<typename t>
56165     bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
56166       t c;
56167       return contains(pixel,n,x,y,z,c);
56168     }
56169 
56170     //! Test if one of the image list contains the specified referenced value.
56171     /**
56172        \param pixel Reference to pixel value to test.
56173        \param[out] n Index of image containing the pixel value, if test succeeds.
56174        \param[out] x X-coordinate of the pixel value, if test succeeds.
56175        \param[out] y Y-coordinate of the pixel value, if test succeeds.
56176        \note If true, set coordinates (n,x,y).
56177     **/
56178     template<typename t>
56179     bool contains(const T& pixel, t& n, t& x, t&y) const {
56180       t z, c;
56181       return contains(pixel,n,x,y,z,c);
56182     }
56183 
56184     //! Test if one of the image list contains the specified referenced value.
56185     /**
56186        \param pixel Reference to pixel value to test.
56187        \param[out] n Index of image containing the pixel value, if test succeeds.
56188        \param[out] x X-coordinate of the pixel value, if test succeeds.
56189        \note If true, set coordinates (n,x).
56190     **/
56191     template<typename t>
56192     bool contains(const T& pixel, t& n, t& x) const {
56193       t y, z, c;
56194       return contains(pixel,n,x,y,z,c);
56195     }
56196 
56197     //! Test if one of the image list contains the specified referenced value.
56198     /**
56199        \param pixel Reference to pixel value to test.
56200        \param[out] n Index of image containing the pixel value, if test succeeds.
56201        \note If true, set coordinates (n).
56202     **/
56203     template<typename t>
56204     bool contains(const T& pixel, t& n) const {
56205       t x, y, z, c;
56206       return contains(pixel,n,x,y,z,c);
56207     }
56208 
56209     //! Test if one of the image list contains the specified referenced value.
56210     /**
56211        \param pixel Reference to pixel value to test.
56212     **/
56213     bool contains(const T& pixel) const {
56214       unsigned int n, x, y, z, c;
56215       return contains(pixel,n,x,y,z,c);
56216     }
56217 
56218     //! Test if the list contains the image 'img'.
56219     /**
56220        \param img Reference to image to test.
56221        \param[out] n Index of image in the list, if test succeeds.
56222        \note If true, returns the position (n) of the image in the list.
56223     **/
56224     template<typename t>
56225     bool contains(const CImg<T>& img, t& n) const {
56226       if (is_empty()) return false;
56227       const CImg<T> *const ptr = &img;
56228       cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; }
56229       return false;
56230     }
56231 
56232     //! Test if the list contains the image img.
56233     /**
56234        \param img Reference to image to test.
56235     **/
56236     bool contains(const CImg<T>& img) const {
56237       unsigned int n;
56238       return contains(img,n);
56239     }
56240 
56241     //@}
56242     //-------------------------------------
56243     //
56244     //! \name Mathematical Functions
56245     //@{
56246     //-------------------------------------
56247 
56248     //! Return a reference to the minimum pixel value of the instance list.
56249     /**
56250     **/
56251     T& min() {
56252       if (is_empty())
56253         throw CImgInstanceException(_cimglist_instance
56254                                     "min(): Empty instance.",
56255                                     cimglist_instance);
56256       T *ptr_min = _data->_data;
56257       T min_value = *ptr_min;
56258       cimglist_for(*this,l) {
56259         const CImg<T>& img = _data[l];
56260         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
56261       }
56262       return *ptr_min;
56263     }
56264 
56265     //! Return a reference to the minimum pixel value of the instance list \const.
56266     const T& min() const {
56267       if (is_empty())
56268         throw CImgInstanceException(_cimglist_instance
56269                                     "min(): Empty instance.",
56270                                     cimglist_instance);
56271       const T *ptr_min = _data->_data;
56272       T min_value = *ptr_min;
56273       cimglist_for(*this,l) {
56274         const CImg<T>& img = _data[l];
56275         cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
56276       }
56277       return *ptr_min;
56278     }
56279 
56280     //! Return a reference to the maximum pixel value of the instance list.
56281     /**
56282     **/
56283     T& max() {
56284       if (is_empty())
56285         throw CImgInstanceException(_cimglist_instance
56286                                     "max(): Empty instance.",
56287                                     cimglist_instance);
56288       T *ptr_max = _data->_data;
56289       T max_value = *ptr_max;
56290       cimglist_for(*this,l) {
56291         const CImg<T>& img = _data[l];
56292         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
56293       }
56294       return *ptr_max;
56295     }
56296 
56297     //! Return a reference to the maximum pixel value of the instance list \const.
56298     const T& max() const {
56299       if (is_empty())
56300         throw CImgInstanceException(_cimglist_instance
56301                                     "max(): Empty instance.",
56302                                     cimglist_instance);
56303       const T *ptr_max = _data->_data;
56304       T max_value = *ptr_max;
56305       cimglist_for(*this,l) {
56306         const CImg<T>& img = _data[l];
56307         cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
56308       }
56309       return *ptr_max;
56310     }
56311 
56312     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well.
56313     /**
56314        \param[out] max_val Value of the maximum value found.
56315     **/
56316     template<typename t>
56317     T& min_max(t& max_val) {
56318       if (is_empty())
56319         throw CImgInstanceException(_cimglist_instance
56320                                     "min_max(): Empty instance.",
56321                                     cimglist_instance);
56322       T *ptr_min = _data->_data;
56323       T min_value = *ptr_min, max_value = min_value;
56324       cimglist_for(*this,l) {
56325         const CImg<T>& img = _data[l];
56326         cimg_for(img,ptrs,T) {
56327           const T val = *ptrs;
56328           if (val<min_value) { min_value = val; ptr_min = ptrs; }
56329           if (val>max_value) max_value = val;
56330         }
56331       }
56332       max_val = (t)max_value;
56333       return *ptr_min;
56334     }
56335 
56336     //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const.
56337     /**
56338        \param[out] max_val Value of the maximum value found.
56339     **/
56340     template<typename t>
56341     const T& min_max(t& max_val) const {
56342       if (is_empty())
56343         throw CImgInstanceException(_cimglist_instance
56344                                     "min_max(): Empty instance.",
56345                                     cimglist_instance);
56346       const T *ptr_min = _data->_data;
56347       T min_value = *ptr_min, max_value = min_value;
56348       cimglist_for(*this,l) {
56349         const CImg<T>& img = _data[l];
56350         cimg_for(img,ptrs,T) {
56351           const T val = *ptrs;
56352           if (val<min_value) { min_value = val; ptr_min = ptrs; }
56353           if (val>max_value) max_value = val;
56354         }
56355       }
56356       max_val = (t)max_value;
56357       return *ptr_min;
56358     }
56359 
56360     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well.
56361     /**
56362        \param[out] min_val Value of the minimum value found.
56363     **/
56364     template<typename t>
56365     T& max_min(t& min_val) {
56366       if (is_empty())
56367         throw CImgInstanceException(_cimglist_instance
56368                                     "max_min(): Empty instance.",
56369                                     cimglist_instance);
56370       T *ptr_max = _data->_data;
56371       T min_value = *ptr_max, max_value = min_value;
56372       cimglist_for(*this,l) {
56373         const CImg<T>& img = _data[l];
56374         cimg_for(img,ptrs,T) {
56375           const T val = *ptrs;
56376           if (val>max_value) { max_value = val; ptr_max = ptrs; }
56377           if (val<min_value) min_value = val;
56378         }
56379       }
56380       min_val = (t)min_value;
56381       return *ptr_max;
56382     }
56383 
56384     //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const.
56385     template<typename t>
56386     const T& max_min(t& min_val) const {
56387       if (is_empty())
56388         throw CImgInstanceException(_cimglist_instance
56389                                     "max_min(): Empty instance.",
56390                                     cimglist_instance);
56391       const T *ptr_max = _data->_data;
56392       T min_value = *ptr_max, max_value = min_value;
56393       cimglist_for(*this,l) {
56394         const CImg<T>& img = _data[l];
56395         cimg_for(img,ptrs,T) {
56396           const T val = *ptrs;
56397           if (val>max_value) { max_value = val; ptr_max = ptrs; }
56398           if (val<min_value) min_value = val;
56399         }
56400       }
56401       min_val = (t)min_value;
56402       return *ptr_max;
56403     }
56404 
56405     //@}
56406     //---------------------------
56407     //
56408     //! \name List Manipulation
56409     //@{
56410     //---------------------------
56411 
56412     //! Insert a copy of the image \c img into the current image list, at position \c pos.
56413     /**
56414         \param img Image to insert a copy to the list.
56415         \param pos Index of the insertion.
56416         \param is_shared Tells if the inserted image is a shared copy of \c img or not.
56417     **/
56418     template<typename t>
56419     CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
56420       const unsigned int npos = pos==~0U?_width:pos;
56421       if (npos>_width)
56422         throw CImgArgumentException(_cimglist_instance
56423                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
56424                                     "at position %u.",
56425                                     cimglist_instance,
56426                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
56427       if (is_shared)
56428         throw CImgArgumentException(_cimglist_instance
56429                                     "insert(): Invalid insertion request of specified shared image "
56430                                     "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).",
56431                                     cimglist_instance,
56432                                     img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
56433 
56434       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
56435                                                                         (_allocated_width=16)]:0;
56436       if (!_data) { // Insert new element into empty list.
56437         _data = new_data;
56438         *_data = img;
56439       } else {
56440         if (new_data) { // Insert with re-allocation.
56441           if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
56442           if (npos!=_width - 1)
56443             std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
56444           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
56445           delete[] _data;
56446           _data = new_data;
56447         } else if (npos!=_width - 1) // Insert without re-allocation.
56448           std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
56449         _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
56450         _data[npos]._data = 0;
56451         _data[npos] = img;
56452       }
56453       return *this;
56454     }
56455 
56456     //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization.
56457     CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
56458       const unsigned int npos = pos==~0U?_width:pos;
56459       if (npos>_width)
56460         throw CImgArgumentException(_cimglist_instance
56461                                     "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
56462                                     "at position %u.",
56463                                     cimglist_instance,
56464                                     img._width,img._height,img._depth,img._spectrum,img._data,npos);
56465       CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
56466                                                                         (_allocated_width=16)]:0;
56467       if (!_data) { // Insert new element into empty list.
56468         _data = new_data;
56469         if (is_shared && img) {
56470           _data->_width = img._width;
56471           _data->_height = img._height;
56472           _data->_depth = img._depth;
56473           _data->_spectrum = img._spectrum;
56474           _data->_is_shared = true;
56475           _data->_data = img._data;
56476         } else *_data = img;
56477       }
56478       else {
56479         if (new_data) { // Insert with re-allocation.
56480           if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
56481           if (npos!=_width - 1)
56482             std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
56483           if (is_shared && img) {
56484             new_data[npos]._width = img._width;
56485             new_data[npos]._height = img._height;
56486             new_data[npos]._depth = img._depth;
56487             new_data[npos]._spectrum = img._spectrum;
56488             new_data[npos]._is_shared = true;
56489             new_data[npos]._data = img._data;
56490           } else {
56491             new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0;
56492             new_data[npos]._data = 0;
56493             new_data[npos] = img;
56494           }
56495           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
56496           delete[] _data;
56497           _data = new_data;
56498         } else { // Insert without re-allocation.
56499           if (npos!=_width - 1)
56500             std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
56501           if (is_shared && img) {
56502             _data[npos]._width = img._width;
56503             _data[npos]._height = img._height;
56504             _data[npos]._depth = img._depth;
56505             _data[npos]._spectrum = img._spectrum;
56506             _data[npos]._is_shared = true;
56507             _data[npos]._data = img._data;
56508           } else {
56509             _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
56510             _data[npos]._data = 0;
56511             _data[npos] = img;
56512           }
56513         }
56514       }
56515       return *this;
56516     }
56517 
56518     //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance.
56519     template<typename t>
56520     CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
56521       return (+*this).insert(img,pos,is_shared);
56522     }
56523 
56524     //! Insert n empty images img into the current image list, at position \p pos.
56525     /**
56526        \param n Number of empty images to insert.
56527        \param pos Index of the insertion.
56528     **/
56529     CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
56530       CImg<T> empty;
56531       if (!n) return *this;
56532       const unsigned int npos = pos==~0U?_width:pos;
56533       for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
56534       return *this;
56535     }
56536 
56537     //! Insert n empty images img into the current image list, at position \p pos \newinstance.
56538     CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
56539       return (+*this).insert(n,pos);
56540     }
56541 
56542     //! Insert \c n copies of the image \c img into the current image list, at position \c pos.
56543     /**
56544        \param n Number of image copies to insert.
56545        \param img Image to insert by copy.
56546        \param pos Index of the insertion.
56547        \param is_shared Tells if inserted images are shared copies of \c img or not.
56548     **/
56549     template<typename t>
56550     CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
56551                         const bool is_shared=false) {
56552       if (!n) return *this;
56553       const unsigned int npos = pos==~0U?_width:pos;
56554       insert(img,npos,is_shared);
56555       for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos + i,is_shared);
56556       return *this;
56557     }
56558 
56559     //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
56560     template<typename t>
56561     CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
56562                            const bool is_shared=false) const {
56563       return (+*this).insert(n,img,pos,is_shared);
56564     }
56565 
56566     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos.
56567     /**
56568       \param list Image list to insert.
56569       \param pos Index of the insertion.
56570       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
56571     **/
56572     template<typename t>
56573     CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
56574       const unsigned int npos = pos==~0U?_width:pos;
56575       if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared);
56576       else insert(CImgList<T>(list),npos,is_shared);
56577       return *this;
56578     }
56579 
56580     //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance.
56581     template<typename t>
56582     CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
56583       return (+*this).insert(list,pos,is_shared);
56584     }
56585 
56586     //! Insert n copies of the list \c list at position \c pos of the current list.
56587     /**
56588       \param n Number of list copies to insert.
56589       \param list Image list to insert.
56590       \param pos Index of the insertion.
56591       \param is_shared Tells if inserted images are shared copies of images of \c list or not.
56592     **/
56593     template<typename t>
56594     CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
56595                         const bool is_shared=false) {
56596       if (!n) return *this;
56597       const unsigned int npos = pos==~0U?_width:pos;
56598       for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
56599       return *this;
56600     }
56601 
56602     //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
56603     template<typename t>
56604     CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
56605                            const bool is_shared=false) const {
56606       return (+*this).insert(n,list,pos,is_shared);
56607     }
56608 
56609     //! Remove all images between from indexes.
56610     /**
56611       \param pos1 Starting index of the removal.
56612       \param pos2 Ending index of the removal.
56613     **/
56614     CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
56615       const unsigned int
56616         npos1 = pos1<pos2?pos1:pos2,
56617         tpos2 = pos1<pos2?pos2:pos1,
56618         npos2 = tpos2<_width?tpos2:_width - 1;
56619       if (npos1>=_width)
56620         throw CImgArgumentException(_cimglist_instance
56621                                     "remove(): Invalid remove request at positions %u->%u.",
56622                                     cimglist_instance,
56623                                     npos1,tpos2);
56624       else {
56625         if (tpos2>=_width)
56626           throw CImgArgumentException(_cimglist_instance
56627                                       "remove(): Invalid remove request at positions %u->%u.",
56628                                       cimglist_instance,
56629                                       npos1,tpos2);
56630 
56631         for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
56632         const unsigned int nb = 1 + npos2 - npos1;
56633         if (!(_width-=nb)) return assign();
56634         if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation.
56635           if (npos1!=_width)
56636             std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
56637           std::memset((void*)(_data + _width),0,sizeof(CImg<T>)*nb);
56638         } else { // Removing items with reallocation.
56639           _allocated_width>>=2;
56640           while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
56641           CImg<T> *const new_data = new CImg<T>[_allocated_width];
56642           if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos1);
56643           if (npos1!=_width)
56644             std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
56645           if (_width!=_allocated_width)
56646             std::memset((void*)(new_data + _width),0,sizeof(CImg<T>)*(_allocated_width - _width));
56647           std::memset((void*)_data,0,sizeof(CImg<T>)*(_width + nb));
56648           delete[] _data;
56649           _data = new_data;
56650         }
56651       }
56652       return *this;
56653     }
56654 
56655     //! Remove all images between from indexes \newinstance.
56656     CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
56657       return (+*this).remove(pos1,pos2);
56658     }
56659 
56660     //! Remove image at index \c pos from the image list.
56661     /**
56662       \param pos Index of the image to remove.
56663     **/
56664     CImgList<T>& remove(const unsigned int pos) {
56665       return remove(pos,pos);
56666     }
56667 
56668     //! Remove image at index \c pos from the image list \newinstance.
56669     CImgList<T> get_remove(const unsigned int pos) const {
56670       return (+*this).remove(pos);
56671     }
56672 
56673     //! Remove last image.
56674     /**
56675     **/
56676     CImgList<T>& remove() {
56677       return remove(_width - 1);
56678     }
56679 
56680     //! Remove last image \newinstance.
56681     CImgList<T> get_remove() const {
56682       return (+*this).remove();
56683     }
56684 
56685     //! Reverse list order.
56686     CImgList<T>& reverse() {
56687       for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]);
56688       return *this;
56689     }
56690 
56691     //! Reverse list order \newinstance.
56692     CImgList<T> get_reverse() const {
56693       return (+*this).reverse();
56694     }
56695 
56696     //! Return a sublist.
56697     /**
56698       \param pos0 Starting index of the sublist.
56699       \param pos1 Ending index of the sublist.
56700     **/
56701     CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
56702       return get_images(pos0,pos1).move_to(*this);
56703     }
56704 
56705     //! Return a sublist \newinstance.
56706     CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
56707       if (pos0>pos1 || pos1>=_width)
56708         throw CImgArgumentException(_cimglist_instance
56709                                     "images(): Specified sub-list indices (%u->%u) are out of bounds.",
56710                                     cimglist_instance,
56711                                     pos0,pos1);
56712       CImgList<T> res(pos1 - pos0 + 1);
56713       cimglist_for(res,l) res[l].assign(_data[pos0 + l]);
56714       return res;
56715     }
56716 
56717     //! Return a shared sublist.
56718     /**
56719       \param pos0 Starting index of the sublist.
56720       \param pos1 Ending index of the sublist.
56721     **/
56722     CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
56723       if (pos0>pos1 || pos1>=_width)
56724         throw CImgArgumentException(_cimglist_instance
56725                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
56726                                     cimglist_instance,
56727                                     pos0,pos1);
56728       CImgList<T> res(pos1 - pos0 + 1);
56729       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
56730       return res;
56731     }
56732 
56733     //! Return a shared sublist \newinstance.
56734     const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
56735       if (pos0>pos1 || pos1>=_width)
56736         throw CImgArgumentException(_cimglist_instance
56737                                     "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
56738                                     cimglist_instance,
56739                                     pos0,pos1);
56740       CImgList<T> res(pos1 - pos0 + 1);
56741       cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
56742       return res;
56743     }
56744 
56745     //! Return a single image which is the appending of all images of the current CImgList instance.
56746     /**
56747        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
56748        \param align Appending alignment.
56749     **/
56750     CImg<T> get_append(const char axis, const float align=0) const {
56751       if (is_empty()) return CImg<T>();
56752       if (_width==1) return +((*this)[0]);
56753       unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
56754       CImg<T> res;
56755       switch (cimg::lowercase(axis)) {
56756       case 'x' : { // Along the X-axis.
56757         cimglist_for(*this,l) {
56758           const CImg<T>& img = (*this)[l];
56759           if (img) {
56760             dx+=img._width;
56761             dy = std::max(dy,img._height);
56762             dz = std::max(dz,img._depth);
56763             dc = std::max(dc,img._spectrum);
56764           }
56765         }
56766         res.assign(dx,dy,dz,dc,(T)0);
56767         if (res) cimglist_for(*this,l) {
56768             const CImg<T>& img = (*this)[l];
56769             if (img) res.draw_image(pos,
56770                                     (int)(align*(dy - img._height)),
56771                                     (int)(align*(dz - img._depth)),
56772                                     (int)(align*(dc - img._spectrum)),
56773                                     img);
56774             pos+=img._width;
56775           }
56776       } break;
56777       case 'y' : { // Along the Y-axis.
56778         cimglist_for(*this,l) {
56779           const CImg<T>& img = (*this)[l];
56780           if (img) {
56781             dx = std::max(dx,img._width);
56782             dy+=img._height;
56783             dz = std::max(dz,img._depth);
56784             dc = std::max(dc,img._spectrum);
56785           }
56786         }
56787         res.assign(dx,dy,dz,dc,(T)0);
56788         if (res) cimglist_for(*this,l) {
56789             const CImg<T>& img = (*this)[l];
56790             if (img) res.draw_image((int)(align*(dx - img._width)),
56791                                     pos,
56792                                     (int)(align*(dz - img._depth)),
56793                                     (int)(align*(dc - img._spectrum)),
56794                                     img);
56795             pos+=img._height;
56796           }
56797       } break;
56798       case 'z' : { // Along the Z-axis.
56799         cimglist_for(*this,l) {
56800           const CImg<T>& img = (*this)[l];
56801           if (img) {
56802             dx = std::max(dx,img._width);
56803             dy = std::max(dy,img._height);
56804             dz+=img._depth;
56805             dc = std::max(dc,img._spectrum);
56806           }
56807         }
56808         res.assign(dx,dy,dz,dc,(T)0);
56809         if (res) cimglist_for(*this,l) {
56810             const CImg<T>& img = (*this)[l];
56811             if (img) res.draw_image((int)(align*(dx - img._width)),
56812                                     (int)(align*(dy - img._height)),
56813                                     pos,
56814                                     (int)(align*(dc - img._spectrum)),
56815                                     img);
56816             pos+=img._depth;
56817           }
56818       } break;
56819       default : { // Along the C-axis.
56820         cimglist_for(*this,l) {
56821           const CImg<T>& img = (*this)[l];
56822           if (img) {
56823             dx = std::max(dx,img._width);
56824             dy = std::max(dy,img._height);
56825             dz = std::max(dz,img._depth);
56826             dc+=img._spectrum;
56827           }
56828         }
56829         res.assign(dx,dy,dz,dc,(T)0);
56830         if (res) cimglist_for(*this,l) {
56831             const CImg<T>& img = (*this)[l];
56832             if (img) res.draw_image((int)(align*(dx - img._width)),
56833                                     (int)(align*(dy - img._height)),
56834                                     (int)(align*(dz - img._depth)),
56835                                     pos,
56836                                     img);
56837             pos+=img._spectrum;
56838           }
56839       }
56840       }
56841       return res;
56842     }
56843 
56844     //! Return a list where each image has been split along the specified axis.
56845     /**
56846         \param axis Axis to split images along.
56847         \param nb Number of spliting parts for each image.
56848     **/
56849     CImgList<T>& split(const char axis, const int nb=-1) {
56850       return get_split(axis,nb).move_to(*this);
56851     }
56852 
56853     //! Return a list where each image has been split along the specified axis \newinstance.
56854     CImgList<T> get_split(const char axis, const int nb=-1) const {
56855       CImgList<T> res;
56856       cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
56857       return res;
56858     }
56859 
56860     //! Insert image at the end of the list.
56861     /**
56862       \param img Image to insert.
56863     **/
56864     template<typename t>
56865     CImgList<T>& push_back(const CImg<t>& img) {
56866       return insert(img);
56867     }
56868 
56869     //! Insert image at the front of the list.
56870     /**
56871       \param img Image to insert.
56872     **/
56873     template<typename t>
56874     CImgList<T>& push_front(const CImg<t>& img) {
56875       return insert(img,0);
56876     }
56877 
56878     //! Insert list at the end of the current list.
56879     /**
56880       \param list List to insert.
56881     **/
56882     template<typename t>
56883     CImgList<T>& push_back(const CImgList<t>& list) {
56884       return insert(list);
56885     }
56886 
56887     //! Insert list at the front of the current list.
56888     /**
56889       \param list List to insert.
56890     **/
56891     template<typename t>
56892     CImgList<T>& push_front(const CImgList<t>& list) {
56893       return insert(list,0);
56894     }
56895 
56896     //! Remove last image.
56897     /**
56898     **/
56899     CImgList<T>& pop_back() {
56900       return remove(_width - 1);
56901     }
56902 
56903     //! Remove first image.
56904     /**
56905     **/
56906     CImgList<T>& pop_front() {
56907       return remove(0);
56908     }
56909 
56910     //! Remove image pointed by iterator.
56911     /**
56912       \param iter Iterator pointing to the image to remove.
56913     **/
56914     CImgList<T>& erase(const iterator iter) {
56915       return remove(iter - _data);
56916     }
56917 
56918     //@}
56919     //----------------------------------
56920     //
56921     //! \name Data Input
56922     //@{
56923     //----------------------------------
56924 
56925     //! Display a simple interactive interface to select images or sublists.
56926     /**
56927        \param disp Window instance to display selection and user interface.
56928        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
56929        \param axis Axis along whom images are appended for visualization.
56930        \param align Alignment setting when images have not all the same size.
56931        \param exit_on_anykey Exit function when any key is pressed.
56932        \return A one-column vector containing the selected image indexes.
56933     **/
56934     CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
56935                           const char axis='x', const float align=0,
56936                           const bool exit_on_anykey=false) const {
56937       return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false);
56938     }
56939 
56940     //! Display a simple interactive interface to select images or sublists.
56941     /**
56942        \param title Title of a new window used to display selection and user interface.
56943        \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
56944        \param axis Axis along whom images are appended for visualization.
56945        \param align Alignment setting when images have not all the same size.
56946        \param exit_on_anykey Exit function when any key is pressed.
56947        \return A one-column vector containing the selected image indexes.
56948     **/
56949     CImg<intT> get_select(const char *const title, const bool feature_type=true,
56950                           const char axis='x', const float align=0,
56951                           const bool exit_on_anykey=false) const {
56952       CImgDisplay disp;
56953       return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false);
56954     }
56955 
56956     CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type,
56957                        const char axis, const float align, const bool exit_on_anykey,
56958                        const unsigned int orig, const bool resize_disp,
56959                        const bool exit_on_rightbutton, const bool exit_on_wheel) const {
56960       if (is_empty())
56961         throw CImgInstanceException(_cimglist_instance
56962                                     "select(): Empty instance.",
56963                                     cimglist_instance);
56964 
56965       // Create image correspondence table and get list dimensions for visualization.
56966       CImgList<uintT> _indices;
56967       unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
56968       cimglist_for(*this,l) {
56969         const CImg<T>& img = _data[l];
56970         const unsigned int
56971           w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
56972           h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
56973         if (w>max_width) max_width = w;
56974         if (h>max_height) max_height = h;
56975         sum_width+=w; sum_height+=h;
56976         if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
56977         else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
56978       }
56979       const CImg<uintT> indices0 = _indices>'x';
56980 
56981       // Create display window.
56982       if (!disp) {
56983         if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
56984         else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
56985         if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
56986       } else if (title) disp.set_title("%s",title);
56987       if (resize_disp) {
56988         if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
56989         else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
56990       }
56991 
56992       const unsigned int old_normalization = disp.normalization();
56993       bool old_is_resized = disp.is_resized();
56994       disp._normalization = 0;
56995       disp.show().set_key(0);
56996       static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
56997 
56998       // Enter event loop.
56999       CImg<ucharT> visu0, visu;
57000       CImg<uintT> indices;
57001       CImg<intT> positions(_width,4,1,1,-1);
57002       int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1;
57003       bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
57004       unsigned int key = 0;
57005 
57006       while (!is_selected && !disp.is_closed() && !key) {
57007 
57008         // Create background image.
57009         if (!visu0) {
57010           visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
57011           (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
57012           unsigned int ind = 0;
57013           const CImg<T> onexone(1,1,1,1,(T)0);
57014           if (axis=='x')
57015             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=4))
57016             cimglist_for(*this,ind) {
57017               unsigned int x0 = 0;
57018               while (x0<visu0._width && indices[x0++]!=(unsigned int)ind) {}
57019               unsigned int x1 = x0;
57020               while (x1<visu0._width && indices[x1++]==(unsigned int)ind) {}
57021               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
57022               CImg<ucharT> res;
57023               src.__get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
57024                 move_to(res);
57025               const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
57026               res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
57027               positions(ind,0) = positions(ind,2) = (int)x0;
57028               positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height()));
57029               positions(ind,2)+=res._width;
57030               positions(ind,3)+=res._height - 1;
57031               visu0.draw_image(positions(ind,0),positions(ind,1),res);
57032             }
57033           else
57034             cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=4))
57035             cimglist_for(*this,ind) {
57036               unsigned int y0 = 0;
57037               while (y0<visu0._height && indices[y0++]!=(unsigned int)ind) {}
57038               unsigned int y1 = y0;
57039               while (y1<visu0._height && indices[y1++]==(unsigned int)ind) {}
57040               const CImg<T> &src = _data[ind]?_data[ind]:onexone;
57041               CImg<ucharT> res;
57042               src.__get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
57043                 move_to(res);
57044               const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
57045               res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100);
57046               positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width()));
57047               positions(ind,1) = positions(ind,3) = (int)y0;
57048               positions(ind,2)+=res._width - 1;
57049               positions(ind,3)+=res._height;
57050               visu0.draw_image(positions(ind,0),positions(ind,1),res);
57051             }
57052           if (axis=='x') --positions(ind,2); else --positions(ind,3);
57053           update_display = true;
57054         }
57055 
57056         if (!visu || oindice0!=indice0 || oindice1!=indice1) {
57057           if (indice0>=0 && indice1>=0) {
57058             visu.assign(visu0,false);
57059             const int indm = std::min(indice0,indice1), indM = std::max(indice0,indice1);
57060             for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
57061                 visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
57062                                     background_color,0.2f);
57063                 if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
57064                     (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
57065                   visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
57066                                       foreground_color,0.9f,0xAAAAAAAA);
57067               }
57068             const int yt = (int)text_down?visu.height() - 13:0;
57069             if (is_clicked) visu.draw_text(0,yt," Images #%u - #%u, Size = %u",
57070                                            foreground_color,background_color,0.7f,13,
57071                                            orig + indm,orig + indM,indM - indm + 1);
57072             else visu.draw_text(0,yt," Image #%u (%u,%u,%u,%u)",foreground_color,background_color,0.7f,13,
57073                                 orig + indice0,
57074                                 _data[indice0]._width,
57075                                 _data[indice0]._height,
57076                                 _data[indice0]._depth,
57077                                 _data[indice0]._spectrum);
57078             update_display = true;
57079           } else visu.assign();
57080         }
57081         if (!visu) { visu.assign(visu0,true); update_display = true; }
57082         if (update_display) { visu.display(disp); update_display = false; }
57083         disp.wait();
57084 
57085         // Manage user events.
57086         const int xm = disp.mouse_x(), ym = disp.mouse_y();
57087         int indice = -1;
57088 
57089         if (xm>=0) {
57090           indice = (int)indices(axis=='x'?xm:ym);
57091           if (disp.button()&1) {
57092             if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; }
57093             oindice1 = indice1; indice1 = indice;
57094             if (!feature_type) is_selected = true;
57095           } else {
57096             if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; }
57097             else is_selected = true;
57098           }
57099         } else {
57100           if (is_clicked) {
57101             if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
57102             else indice1 = -1;
57103           } else indice0 = indice1 = -1;
57104         }
57105 
57106         if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
57107         if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; }
57108         if (disp.wheel() && exit_on_wheel) is_selected = true;
57109 
57110         CImg<charT> filename(32);
57111         switch (key = disp.key()) {
57112 #if cimg_OS!=2
57113         case cimg::keyCTRLRIGHT :
57114 #endif
57115         case 0 : case cimg::keyCTRLLEFT : key = 0; break;
57116         case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57117             disp.set_fullscreen(false).
57118               resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
57119                      CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
57120               _is_resized = true;
57121             disp.set_key(key,false); key = 0; visu0.assign();
57122           } break;
57123         case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57124             disp.set_fullscreen(false).
57125               resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
57126             disp.set_key(key,false); key = 0; visu0.assign();
57127           } break;
57128         case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57129             disp.set_fullscreen(false).
57130               resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
57131               _is_resized = true;
57132             disp.set_key(key,false); key = 0; visu0.assign();
57133           } break;
57134         case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57135             disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
57136             disp.set_key(key,false); key = 0; visu0.assign();
57137           } break;
57138         case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57139             static unsigned int snap_number = 0;
57140             std::FILE *file;
57141             do {
57142               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++);
57143               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
57144             } while (file);
57145             if (visu0) {
57146               (+visu0).draw_text(0,0," Saving snapshot... ",
57147                                  foreground_color,background_color,0.7f,13).display(disp);
57148               visu0.save(filename);
57149               (+visu0).draw_text(0,0," Snapshot '%s' saved. ",
57150                                  foreground_color,background_color,0.7f,13,filename._data).display(disp);
57151             }
57152             disp.set_key(key,false).wait(); key = 0;
57153           } break;
57154         case cimg::keyO :
57155           if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
57156             static unsigned int snap_number = 0;
57157             std::FILE *file;
57158             do {
57159 #ifdef cimg_use_zlib
57160               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++);
57161 #else
57162               cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++);
57163 #endif
57164               if ((file=std_fopen(filename,"r"))!=0) cimg::fclose(file);
57165             } while (file);
57166             (+visu0).draw_text(0,0," Saving instance... ",
57167                                foreground_color,background_color,0.7f,13).display(disp);
57168             save(filename);
57169             (+visu0).draw_text(0,0," Instance '%s' saved. ",
57170                                foreground_color,background_color,0.7f,13,filename._data).display(disp);
57171             disp.set_key(key,false).wait(); key = 0;
57172           } break;
57173         }
57174         if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
57175         if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
57176         else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }}
57177         if (!exit_on_anykey && key && key!=cimg::keyESC &&
57178             (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
57179           key = 0;
57180         }
57181       }
57182       CImg<intT> res(1,2,1,1,-1);
57183       if (is_selected) {
57184         if (feature_type) res.fill(std::min(indice0,indice1),std::max(indice0,indice1));
57185         else res.fill(indice0);
57186       }
57187       if (!(disp.button()&2)) disp.set_button();
57188       disp._normalization = old_normalization;
57189       disp._is_resized = old_is_resized;
57190       disp.set_key(key);
57191       return res;
57192     }
57193 
57194     //! Load a list from a file.
57195     /**
57196      \param filename Filename to read data from.
57197     **/
57198     CImgList<T>& load(const char *const filename) {
57199       if (!filename)
57200         throw CImgArgumentException(_cimglist_instance
57201                                     "load(): Specified filename is (null).",
57202                                     cimglist_instance);
57203 
57204       if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
57205         CImg<charT> filename_local(256);
57206         load(cimg::load_network(filename,filename_local));
57207         std::remove(filename_local);
57208         return *this;
57209       }
57210 
57211       const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.');
57212       const char *const ext = cimg::split_filename(filename);
57213       const unsigned int omode = cimg::exception_mode();
57214       cimg::exception_mode(0);
57215       bool is_loaded = true;
57216       try {
57217 #ifdef cimglist_load_plugin
57218         cimglist_load_plugin(filename);
57219 #endif
57220 #ifdef cimglist_load_plugin1
57221         cimglist_load_plugin1(filename);
57222 #endif
57223 #ifdef cimglist_load_plugin2
57224         cimglist_load_plugin2(filename);
57225 #endif
57226 #ifdef cimglist_load_plugin3
57227         cimglist_load_plugin3(filename);
57228 #endif
57229 #ifdef cimglist_load_plugin4
57230         cimglist_load_plugin4(filename);
57231 #endif
57232 #ifdef cimglist_load_plugin5
57233         cimglist_load_plugin5(filename);
57234 #endif
57235 #ifdef cimglist_load_plugin6
57236         cimglist_load_plugin6(filename);
57237 #endif
57238 #ifdef cimglist_load_plugin7
57239         cimglist_load_plugin7(filename);
57240 #endif
57241 #ifdef cimglist_load_plugin8
57242         cimglist_load_plugin8(filename);
57243 #endif
57244         if (!cimg::strcasecmp(ext,"tif") ||
57245             !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
57246         else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
57247         else if (!cimg::strcasecmp(ext,"cimg") ||
57248                  !cimg::strcasecmp(ext,"cimgz") ||
57249                  !*ext) load_cimg(filename);
57250         else if (!cimg::strcasecmp(ext,"rec") ||
57251                  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
57252         else if (!cimg::strcasecmp(ext,"avi") ||
57253                  !cimg::strcasecmp(ext,"mov") ||
57254                  !cimg::strcasecmp(ext,"asf") ||
57255                  !cimg::strcasecmp(ext,"divx") ||
57256                  !cimg::strcasecmp(ext,"flv") ||
57257                  !cimg::strcasecmp(ext,"mpg") ||
57258                  !cimg::strcasecmp(ext,"m1v") ||
57259                  !cimg::strcasecmp(ext,"m2v") ||
57260                  !cimg::strcasecmp(ext,"m4v") ||
57261                  !cimg::strcasecmp(ext,"mjp") ||
57262                  !cimg::strcasecmp(ext,"mp4") ||
57263                  !cimg::strcasecmp(ext,"mkv") ||
57264                  !cimg::strcasecmp(ext,"mpe") ||
57265                  !cimg::strcasecmp(ext,"movie") ||
57266                  !cimg::strcasecmp(ext,"ogm") ||
57267                  !cimg::strcasecmp(ext,"ogg") ||
57268                  !cimg::strcasecmp(ext,"ogv") ||
57269                  !cimg::strcasecmp(ext,"qt") ||
57270                  !cimg::strcasecmp(ext,"rm") ||
57271                  !cimg::strcasecmp(ext,"vob") ||
57272                  !cimg::strcasecmp(ext,"wmv") ||
57273                  !cimg::strcasecmp(ext,"xvid") ||
57274                  !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
57275         else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
57276         else is_loaded = false;
57277       } catch (CImgIOException&) { is_loaded = false; }
57278 
57279       // If nothing loaded, try to guess file format from magic number in file.
57280       if (!is_loaded && !is_stdin) {
57281         std::FILE *const file = std_fopen(filename,"rb");
57282         if (!file) {
57283           cimg::exception_mode(omode);
57284           throw CImgIOException(_cimglist_instance
57285                                 "load(): Failed to open file '%s'.",
57286                                 cimglist_instance,
57287                                 filename);
57288         }
57289 
57290         const char *const f_type = cimg::ftype(file,filename);
57291         std::fclose(file);
57292         is_loaded = true;
57293         try {
57294           if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
57295           else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
57296           else is_loaded = false;
57297         } catch (CImgIOException&) { is_loaded = false; }
57298       }
57299 
57300       // If nothing loaded, try to load file as a single image.
57301       if (!is_loaded) {
57302         assign(1);
57303         try {
57304           _data->load(filename);
57305         } catch (CImgIOException&) {
57306           cimg::exception_mode(omode);
57307           throw CImgIOException(_cimglist_instance
57308                                 "load(): Failed to recognize format of file '%s'.",
57309                                 cimglist_instance,
57310                                 filename);
57311         }
57312       }
57313       cimg::exception_mode(omode);
57314       return *this;
57315     }
57316 
57317     //! Load a list from a file \newinstance.
57318     static CImgList<T> get_load(const char *const filename) {
57319       return CImgList<T>().load(filename);
57320     }
57321 
57322     //! Load a list from a .cimg file.
57323     /**
57324       \param filename Filename to read data from.
57325     **/
57326     CImgList<T>& load_cimg(const char *const filename) {
57327       return _load_cimg(0,filename);
57328     }
57329 
57330     //! Load a list from a .cimg file \newinstance.
57331     static CImgList<T> get_load_cimg(const char *const filename) {
57332       return CImgList<T>().load_cimg(filename);
57333     }
57334 
57335     //! Load a list from a .cimg file.
57336     /**
57337       \param file File to read data from.
57338     **/
57339     CImgList<T>& load_cimg(std::FILE *const file) {
57340       return _load_cimg(file,0);
57341     }
57342 
57343     //! Load a list from a .cimg file \newinstance.
57344     static CImgList<T> get_load_cimg(std::FILE *const file) {
57345       return CImgList<T>().load_cimg(file);
57346     }
57347 
57348     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
57349 #ifdef cimg_use_zlib
57350 #define _cimgz_load_cimg_case(Tss) { \
57351    Bytef *const cbuf = new Bytef[csiz]; \
57352    cimg::fread(cbuf,csiz,nfile); \
57353    raw.assign(W,H,D,C); \
57354    uLongf destlen = (ulongT)raw.size()*sizeof(Tss); \
57355    uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
57356    delete[] cbuf; \
57357    if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
57358    raw.move_to(img); \
57359 }
57360 #else
57361 #define _cimgz_load_cimg_case(Tss) \
57362    throw CImgIOException(_cimglist_instance \
57363                          "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
57364                          cimglist_instance, \
57365                          filename?filename:"(FILE*)");
57366 #endif
57367 
57368 #define _cimg_load_cimg_case(Ts,Tss) \
57369       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
57370         for (unsigned int l = 0; l<N; ++l) { \
57371           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \
57372           W = H = D = C = 0; csiz = 0; \
57373           if ((err = cimg_sscanf(tmp,"%u %u %u %u #%lu",&W,&H,&D,&C,&csiz))<4) \
57374             throw CImgIOException(_cimglist_instance \
57375                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \
57376                                   cimglist_instance, \
57377                                   W,H,D,C,l,filename?filename:("(FILE*)")); \
57378           if (W*H*D*C>0) { \
57379             CImg<Tss> raw; \
57380             CImg<T> &img = _data[l]; \
57381             if (err==5) _cimgz_load_cimg_case(Tss) \
57382             else { \
57383               img.assign(W,H,D,C); \
57384               T *ptrd = img._data; \
57385               for (ulongT to_read = img.size(); to_read; ) { \
57386                 raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
57387                 cimg::fread(raw._data,raw._width,nfile); \
57388                 if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
57389                 const Tss *ptrs = raw._data; \
57390                 for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
57391                 to_read-=raw._width; \
57392               } \
57393             } \
57394           } \
57395         } \
57396         loaded = true; \
57397       }
57398 
57399       if (!filename && !file)
57400         throw CImgArgumentException(_cimglist_instance
57401                                     "load_cimg(): Specified filename is (null).",
57402                                     cimglist_instance);
57403 
57404       const ulongT cimg_iobuffer = (ulongT)24*1024*1024;
57405       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
57406       bool loaded = false, endian = cimg::endianness();
57407       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
57408       *tmp = *str_pixeltype = *str_endian = 0;
57409       unsigned int j, N = 0, W, H, D, C;
57410       unsigned long csiz;
57411       int i, err;
57412       do {
57413         j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0;
57414       } while (*tmp=='#' && i>=0);
57415       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
57416                         &N,str_pixeltype._data,str_endian._data);
57417       if (err<2) {
57418         if (!file) cimg::fclose(nfile);
57419         throw CImgIOException(_cimglist_instance
57420                               "load_cimg(): CImg header not found in file '%s'.",
57421                               cimglist_instance,
57422                               filename?filename:"(FILE*)");
57423       }
57424       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
57425       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
57426       assign(N);
57427       _cimg_load_cimg_case("bool",bool);
57428       _cimg_load_cimg_case("unsigned_char",unsigned char);
57429       _cimg_load_cimg_case("uchar",unsigned char);
57430       _cimg_load_cimg_case("char",char);
57431       _cimg_load_cimg_case("unsigned_short",unsigned short);
57432       _cimg_load_cimg_case("ushort",unsigned short);
57433       _cimg_load_cimg_case("short",short);
57434       _cimg_load_cimg_case("unsigned_int",unsigned int);
57435       _cimg_load_cimg_case("uint",unsigned int);
57436       _cimg_load_cimg_case("int",int);
57437       _cimg_load_cimg_case("unsigned_long",ulongT);
57438       _cimg_load_cimg_case("ulong",ulongT);
57439       _cimg_load_cimg_case("long",longT);
57440       _cimg_load_cimg_case("unsigned_int64",uint64T);
57441       _cimg_load_cimg_case("uint64",uint64T);
57442       _cimg_load_cimg_case("int64",int64T);
57443       _cimg_load_cimg_case("float",float);
57444       _cimg_load_cimg_case("double",double);
57445 
57446       if (!loaded) {
57447         if (!file) cimg::fclose(nfile);
57448         throw CImgIOException(_cimglist_instance
57449                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
57450                               cimglist_instance,
57451                               str_pixeltype._data,filename?filename:"(FILE*)");
57452       }
57453       if (!file) cimg::fclose(nfile);
57454       return *this;
57455     }
57456 
57457     //! Load a sublist list from a (non compressed) .cimg file.
57458     /**
57459       \param filename Filename to read data from.
57460       \param n0 Starting index of images to read (~0U for max).
57461       \param n1 Ending index of images to read (~0U for max).
57462       \param x0 Starting X-coordinates of image regions to read.
57463       \param y0 Starting Y-coordinates of image regions to read.
57464       \param z0 Starting Z-coordinates of image regions to read.
57465       \param c0 Starting C-coordinates of image regions to read.
57466       \param x1 Ending X-coordinates of image regions to read (~0U for max).
57467       \param y1 Ending Y-coordinates of image regions to read (~0U for max).
57468       \param z1 Ending Z-coordinates of image regions to read (~0U for max).
57469       \param c1 Ending C-coordinates of image regions to read (~0U for max).
57470     **/
57471     CImgList<T>& load_cimg(const char *const filename,
57472                            const unsigned int n0, const unsigned int n1,
57473                            const unsigned int x0, const unsigned int y0,
57474                            const unsigned int z0, const unsigned int c0,
57475                            const unsigned int x1, const unsigned int y1,
57476                            const unsigned int z1, const unsigned int c1) {
57477       return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
57478     }
57479 
57480     //! Load a sublist list from a (non compressed) .cimg file \newinstance.
57481     static CImgList<T> get_load_cimg(const char *const filename,
57482                                      const unsigned int n0, const unsigned int n1,
57483                                      const unsigned int x0, const unsigned int y0,
57484                                      const unsigned int z0, const unsigned int c0,
57485                                      const unsigned int x1, const unsigned int y1,
57486                                      const unsigned int z1, const unsigned int c1) {
57487       return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
57488     }
57489 
57490     //! Load a sub-image list from a (non compressed) .cimg file \overloading.
57491     CImgList<T>& load_cimg(std::FILE *const file,
57492                            const unsigned int n0, const unsigned int n1,
57493                            const unsigned int x0, const unsigned int y0,
57494                            const unsigned int z0, const unsigned int c0,
57495                            const unsigned int x1, const unsigned int y1,
57496                            const unsigned int z1, const unsigned int c1) {
57497       return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
57498     }
57499 
57500     //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
57501     static CImgList<T> get_load_cimg(std::FILE *const file,
57502                                      const unsigned int n0, const unsigned int n1,
57503                                      const unsigned int x0, const unsigned int y0,
57504                                      const unsigned int z0, const unsigned int c0,
57505                                      const unsigned int x1, const unsigned int y1,
57506                                      const unsigned int z1, const unsigned int c1) {
57507       return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
57508     }
57509 
57510     CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
57511 			    const unsigned int n0, const unsigned int n1,
57512 			    const unsigned int x0, const unsigned int y0,
57513                             const unsigned int z0, const unsigned int c0,
57514 			    const unsigned int x1, const unsigned int y1,
57515                             const unsigned int z1, const unsigned int c1) {
57516 #define _cimg_load_cimg_case2(Ts,Tss) \
57517       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
57518         for (unsigned int l = 0; l<=nn1; ++l) { \
57519           j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
57520           W = H = D = C = 0; \
57521           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
57522             throw CImgIOException(_cimglist_instance \
57523                                   "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
57524                                   cimglist_instance, \
57525                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
57526           if (W*H*D*C>0) { \
57527             if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
57528             else { \
57529               const unsigned int \
57530                 _nx1 = nx1==~0U?W - 1:nx1, \
57531                 _ny1 = ny1==~0U?H - 1:ny1, \
57532                 _nz1 = nz1==~0U?D - 1:nz1, \
57533                 _nc1 = nc1==~0U?C - 1:nc1; \
57534               if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
57535                 throw CImgArgumentException(_cimglist_instance \
57536                                             "load_cimg(): Invalid specified coordinates " \
57537                                             "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
57538                                             "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
57539                                             cimglist_instance, \
57540                                             n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
57541               CImg<Tss> raw(1 + _nx1 - nx0); \
57542               CImg<T> &img = _data[l - nn0]; \
57543               img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
57544               T *ptrd = img._data; \
57545               ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
57546               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
57547               for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
57548                 const ulongT skipzb = nz0*W*H*sizeof(Tss); \
57549                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
57550                 for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
57551                   const ulongT skipyb = ny0*W*sizeof(Tss); \
57552                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
57553                   for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
57554                     const ulongT skipxb = nx0*sizeof(Tss); \
57555                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
57556                     cimg::fread(raw._data,raw._width,nfile); \
57557                     if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
57558                     const Tss *ptrs = raw._data; \
57559                     for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
57560                     const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
57561                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
57562                   } \
57563                   const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
57564                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
57565                 } \
57566                 const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
57567                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
57568               } \
57569               const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
57570               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
57571             } \
57572           } \
57573         } \
57574         loaded = true; \
57575       }
57576 
57577       if (!filename && !file)
57578         throw CImgArgumentException(_cimglist_instance
57579                                     "load_cimg(): Specified filename is (null).",
57580                                     cimglist_instance);
57581       unsigned int
57582         nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
57583         nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
57584         ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
57585         nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
57586         nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);
57587 
57588       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
57589       bool loaded = false, endian = cimg::endianness();
57590       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
57591       *tmp = *str_pixeltype = *str_endian = 0;
57592       unsigned int j, N, W, H, D, C;
57593       int i, err;
57594       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
57595       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
57596                         &N,str_pixeltype._data,str_endian._data);
57597       if (err<2) {
57598         if (!file) cimg::fclose(nfile);
57599         throw CImgIOException(_cimglist_instance
57600                               "load_cimg(): CImg header not found in file '%s'.",
57601                               cimglist_instance,
57602                               filename?filename:"(FILE*)");
57603       }
57604       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
57605       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
57606       nn1 = n1==~0U?N - 1:n1;
57607       if (nn1>=N)
57608         throw CImgArgumentException(_cimglist_instance
57609                                     "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
57610                                     "because file '%s' contains only %u images.",
57611                                     cimglist_instance,
57612                                     n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
57613       assign(1 + nn1 - n0);
57614       _cimg_load_cimg_case2("bool",bool);
57615       _cimg_load_cimg_case2("unsigned_char",unsigned char);
57616       _cimg_load_cimg_case2("uchar",unsigned char);
57617       _cimg_load_cimg_case2("char",char);
57618       _cimg_load_cimg_case2("unsigned_short",unsigned short);
57619       _cimg_load_cimg_case2("ushort",unsigned short);
57620       _cimg_load_cimg_case2("short",short);
57621       _cimg_load_cimg_case2("unsigned_int",unsigned int);
57622       _cimg_load_cimg_case2("uint",unsigned int);
57623       _cimg_load_cimg_case2("int",int);
57624       _cimg_load_cimg_case2("unsigned_long",ulongT);
57625       _cimg_load_cimg_case2("ulong",ulongT);
57626       _cimg_load_cimg_case2("long",longT);
57627       _cimg_load_cimg_case2("unsigned_int64",uint64T);
57628       _cimg_load_cimg_case2("uint64",uint64T);
57629       _cimg_load_cimg_case2("int64",int64T);
57630       _cimg_load_cimg_case2("float",float);
57631       _cimg_load_cimg_case2("double",double);
57632       if (!loaded) {
57633         if (!file) cimg::fclose(nfile);
57634         throw CImgIOException(_cimglist_instance
57635                               "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
57636                               cimglist_instance,
57637                               str_pixeltype._data,filename?filename:"(FILE*)");
57638       }
57639       if (!file) cimg::fclose(nfile);
57640       return *this;
57641     }
57642 
57643     //! Load a list from a PAR/REC (Philips) file.
57644     /**
57645       \param filename Filename to read data from.
57646     **/
57647     CImgList<T>& load_parrec(const char *const filename) {
57648       if (!filename)
57649         throw CImgArgumentException(_cimglist_instance
57650                                     "load_parrec(): Specified filename is (null).",
57651                                     cimglist_instance);
57652 
57653       CImg<charT> body(1024), filenamepar(1024), filenamerec(1024);
57654       *body = *filenamepar = *filenamerec = 0;
57655       const char *const ext = cimg::split_filename(filename,body);
57656       if (!std::strcmp(ext,"par")) {
57657         std::strncpy(filenamepar,filename,filenamepar._width - 1);
57658         cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data);
57659       }
57660       if (!std::strcmp(ext,"PAR")) {
57661         std::strncpy(filenamepar,filename,filenamepar._width - 1);
57662         cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data);
57663       }
57664       if (!std::strcmp(ext,"rec")) {
57665         std::strncpy(filenamerec,filename,filenamerec._width - 1);
57666         cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data);
57667       }
57668       if (!std::strcmp(ext,"REC")) {
57669         std::strncpy(filenamerec,filename,filenamerec._width - 1);
57670         cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data);
57671       }
57672       std::FILE *file = cimg::fopen(filenamepar,"r");
57673 
57674       // Parse header file
57675       CImgList<floatT> st_slices;
57676       CImgList<uintT> st_global;
57677       CImg<charT> line(256); *line = 0;
57678       int err;
57679       do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.'));
57680       do {
57681         unsigned int sn,size_x,size_y,pixsize;
57682         float rs,ri,ss;
57683         err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss);
57684         if (err==7) {
57685           CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
57686           unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
57687           if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
57688           else {
57689             CImg<uintT> &vec = st_global[i];
57690             if (size_x>vec[0]) vec[0] = size_x;
57691             if (size_y>vec[1]) vec[1] = size_y;
57692             vec[2] = sn;
57693           }
57694           st_slices[st_slices._width - 1][7] = (float)i;
57695         }
57696       } while (err==7);
57697 
57698       // Read data
57699       std::FILE *file2 = cimg::fopen(filenamerec,"rb");
57700       cimglist_for(st_global,l) {
57701         const CImg<uintT>& vec = st_global[l];
57702         CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
57703       }
57704 
57705       cimglist_for(st_slices,l) {
57706         const CImg<floatT>& vec = st_slices[l];
57707         const unsigned int
57708           sn = (unsigned int)vec[0] - 1,
57709           pixsize = (unsigned int)vec[1],
57710           size_x = (unsigned int)vec[2],
57711           size_y = (unsigned int)vec[3],
57712           imn = (unsigned int)vec[7];
57713         const float ri = vec[4], rs = vec[5], ss = vec[6];
57714         switch (pixsize) {
57715         case 8 : {
57716           CImg<ucharT> buf(size_x,size_y);
57717           cimg::fread(buf._data,size_x*size_y,file2);
57718           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
57719           CImg<T>& img = (*this)[imn];
57720           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
57721         } break;
57722         case 16 : {
57723           CImg<ushortT> buf(size_x,size_y);
57724           cimg::fread(buf._data,size_x*size_y,file2);
57725           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
57726           CImg<T>& img = (*this)[imn];
57727           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
57728         } break;
57729         case 32 : {
57730           CImg<uintT> buf(size_x,size_y);
57731           cimg::fread(buf._data,size_x*size_y,file2);
57732           if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
57733           CImg<T>& img = (*this)[imn];
57734           cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
57735         } break;
57736         default :
57737           cimg::fclose(file);
57738           cimg::fclose(file2);
57739           throw CImgIOException(_cimglist_instance
57740                                 "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
57741                                 cimglist_instance,
57742                                 pixsize,filename);
57743         }
57744       }
57745       cimg::fclose(file);
57746       cimg::fclose(file2);
57747       if (!_width)
57748         throw CImgIOException(_cimglist_instance
57749                               "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
57750                               cimglist_instance,
57751                               filename);
57752       return *this;
57753     }
57754 
57755     //! Load a list from a PAR/REC (Philips) file \newinstance.
57756     static CImgList<T> get_load_parrec(const char *const filename) {
57757       return CImgList<T>().load_parrec(filename);
57758     }
57759 
57760     //! Load a list from a YUV image sequence file.
57761     /**
57762         \param filename Filename to read data from.
57763         \param size_x Width of the images.
57764         \param size_y Height of the images.
57765         \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
57766         \param first_frame Index of first image frame to read.
57767         \param last_frame Index of last image frame to read.
57768         \param step_frame Step applied between each frame.
57769         \param yuv2rgb Apply YUV to RGB transformation during reading.
57770     **/
57771     CImgList<T>& load_yuv(const char *const filename,
57772                           const unsigned int size_x, const unsigned int size_y,
57773                           const unsigned int chroma_subsampling=444,
57774                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57775                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
57776       return _load_yuv(0,filename,size_x,size_y,chroma_subsampling,
57777                        first_frame,last_frame,step_frame,yuv2rgb);
57778     }
57779 
57780     //! Load a list from a YUV image sequence file \newinstance.
57781     static CImgList<T> get_load_yuv(const char *const filename,
57782                                     const unsigned int size_x, const unsigned int size_y=1,
57783                                     const unsigned int chroma_subsampling=444,
57784                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57785                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
57786       return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
57787                                     first_frame,last_frame,step_frame,yuv2rgb);
57788     }
57789 
57790     //! Load a list from an image sequence YUV file \overloading.
57791     CImgList<T>& load_yuv(std::FILE *const file,
57792                           const unsigned int size_x, const unsigned int size_y,
57793                           const unsigned int chroma_subsampling=444,
57794                           const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57795                           const unsigned int step_frame=1, const bool yuv2rgb=true) {
57796       return _load_yuv(file,0,size_x,size_y,chroma_subsampling,
57797                        first_frame,last_frame,step_frame,yuv2rgb);
57798     }
57799 
57800     //! Load a list from an image sequence YUV file \newinstance.
57801     static CImgList<T> get_load_yuv(std::FILE *const file,
57802                                     const unsigned int size_x, const unsigned int size_y=1,
57803                                     const unsigned int chroma_subsampling=444,
57804                                     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57805                                     const unsigned int step_frame=1, const bool yuv2rgb=true) {
57806       return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
57807                                     first_frame,last_frame,step_frame,yuv2rgb);
57808     }
57809 
57810     CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
57811 			   const unsigned int size_x, const unsigned int size_y,
57812                            const unsigned int chroma_subsampling,
57813 			   const unsigned int first_frame, const unsigned int last_frame,
57814 			   const unsigned int step_frame, const bool yuv2rgb) {
57815       if (!filename && !file)
57816         throw CImgArgumentException(_cimglist_instance
57817                                     "load_yuv(): Specified filename is (null).",
57818                                     cimglist_instance);
57819       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
57820         throw CImgArgumentException(_cimglist_instance
57821                                     "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.",
57822                                     cimglist_instance,
57823                                     chroma_subsampling,filename?filename:"(FILE*)");
57824       const unsigned int
57825         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
57826         cfy = chroma_subsampling==420?2:1,
57827 	nfirst_frame = first_frame<last_frame?first_frame:last_frame,
57828 	nlast_frame = first_frame<last_frame?last_frame:first_frame,
57829 	nstep_frame = step_frame?step_frame:1;
57830 
57831       if (!size_x || !size_y || size_x%cfx || size_y%cfy)
57832         throw CImgArgumentException(_cimglist_instance
57833                                     "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.",
57834                                     cimglist_instance,
57835                                     size_x,size_y,filename?filename:"(FILE*)");
57836 
57837       CImg<ucharT> YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2);
57838       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
57839       bool stop_flag = false;
57840       int err;
57841       if (nfirst_frame) {
57842         err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR);
57843         if (err) {
57844           if (!file) cimg::fclose(nfile);
57845           throw CImgIOException(_cimglist_instance
57846                                 "load_yuv(): File '%s' doesn't contain frame number %u.",
57847                                 cimglist_instance,
57848                                 filename?filename:"(FILE*)",nfirst_frame);
57849         }
57850       }
57851       unsigned int frame;
57852       for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) {
57853         YUV.get_shared_channel(0).fill(0);
57854         // *TRY* to read the luminance part, do not replace by cimg::fread!
57855         err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile);
57856         if (err!=(int)(YUV._width*YUV._height)) {
57857           stop_flag = true;
57858           if (err>0)
57859             cimg::warn(_cimglist_instance
57860                        "load_yuv(): File '%s' contains incomplete data or given image dimensions "
57861                        "(%u,%u) are incorrect.",
57862                        cimglist_instance,
57863                        filename?filename:"(FILE*)",size_x,size_y);
57864         } else {
57865           UV.fill(0);
57866           // *TRY* to read the luminance part, do not replace by cimg::fread!
57867           err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile);
57868           if (err!=(int)(UV.size())) {
57869             stop_flag = true;
57870             if (err>0)
57871               cimg::warn(_cimglist_instance
57872                          "load_yuv(): File '%s' contains incomplete data or given image dimensions "
57873                          "(%u,%u) are incorrect.",
57874                          cimglist_instance,
57875                          filename?filename:"(FILE*)",size_x,size_y);
57876           } else {
57877             const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1);
57878             ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2);
57879             const unsigned int wd = YUV._width;
57880             switch (chroma_subsampling) {
57881             case 420 :
57882               cimg_forY(UV,y) {
57883                 cimg_forX(UV,x) {
57884                   const ucharT U = *(ptrs1++), V = *(ptrs2++);
57885                   ptrd1[wd] = U; *(ptrd1)++ = U;
57886                   ptrd1[wd] = U; *(ptrd1)++ = U;
57887                   ptrd2[wd] = V; *(ptrd2)++ = V;
57888                   ptrd2[wd] = V; *(ptrd2)++ = V;
57889                 }
57890                 ptrd1+=wd; ptrd2+=wd;
57891               }
57892               break;
57893             case 422 :
57894               cimg_forXY(UV,x,y) {
57895                 const ucharT U = *(ptrs1++), V = *(ptrs2++);
57896                 *(ptrd1++) = U; *(ptrd1++) = U;
57897                 *(ptrd2++) = V; *(ptrd2++) = V;
57898               }
57899               break;
57900             default :
57901               YUV.draw_image(0,0,0,1,UV);
57902             }
57903             if (yuv2rgb) YUV.YCbCrtoRGB();
57904             insert(YUV);
57905             if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
57906           }
57907         }
57908       }
57909       if (is_empty())
57910         throw CImgIOException(_cimglist_instance
57911                               "load_yuv() : Missing data in file '%s'.",
57912                               cimglist_instance,
57913                               filename?filename:"(FILE*)");
57914       if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame)
57915         cimg::warn(_cimglist_instance
57916                    "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
57917                    cimglist_instance,
57918                    nlast_frame,frame - 1,filename?filename:"(FILE*)");
57919 
57920       if (!file) cimg::fclose(nfile);
57921       return *this;
57922     }
57923 
57924     //! Load an image from a video file, using OpenCV library.
57925     /**
57926       \param filename Filename, as a C-string.
57927       \param first_frame Index of the first frame to read.
57928       \param last_frame Index of the last frame to read.
57929       \param step_frame Step value for frame reading.
57930       \note If step_frame==0, the current video stream is forced to be released (without any frames read).
57931     **/
57932     CImgList<T>& load_video(const char *const filename,
57933                             const unsigned int first_frame=0, const unsigned int last_frame=~0U,
57934                             const unsigned int step_frame=1) {
57935 #ifndef cimg_use_opencv
57936       if (first_frame || last_frame!=~0U || step_frame>1)
57937         throw CImgArgumentException(_cimglist_instance
57938                                     "load_video() : File '%s', arguments 'first_frame', 'last_frame' "
57939                                     "and 'step_frame' can be only set when using OpenCV "
57940                                     "(-Dcimg_use_opencv must be enabled).",
57941                                     cimglist_instance,filename);
57942       return load_ffmpeg_external(filename);
57943 #else
57944       static CvCapture *captures[32] = { 0 };
57945       static CImgList<charT> filenames(32);
57946       static CImg<uintT> positions(32,1,1,1,0);
57947       static int last_used_index = -1;
57948 
57949       // Detect if a video capture already exists for the specified filename.
57950       cimg::mutex(9);
57951       int index = -1;
57952       if (filename) {
57953         if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
57954           index = last_used_index;
57955         } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
57956             index = l; break;
57957           }
57958       } else index = last_used_index;
57959       cimg::mutex(9,0);
57960 
57961       // Release stream if needed.
57962       if (!step_frame || (index>=0 && positions[index]>first_frame)) {
57963         if (index>=0) {
57964           cimg::mutex(9);
57965           cvReleaseCapture(&captures[index]);
57966           captures[index] = 0; filenames[index].assign(); positions[index] = 0;
57967           if (last_used_index==index) last_used_index = -1;
57968           index = -1;
57969           cimg::mutex(9,0);
57970         } else
57971           if (filename)
57972             cimg::warn(_cimglist_instance
57973                        "load_video() : File '%s', no opened video stream associated with filename found.",
57974                        cimglist_instance,filename);
57975           else
57976             cimg::warn(_cimglist_instance
57977                        "load_video() : No opened video stream found.",
57978                        cimglist_instance,filename);
57979         if (!step_frame) return *this;
57980       }
57981 
57982       // Find empty slot for capturing video stream.
57983       if (index<0) {
57984         if (!filename)
57985           throw CImgArgumentException(_cimglist_instance
57986                                       "load_video(): No already open video reader found. You must specify a "
57987                                       "non-(null) filename argument for the first call.",
57988                                       cimglist_instance);
57989         else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
57990         if (index<0)
57991           throw CImgIOException(_cimglist_instance
57992                                 "load_video(): File '%s', no video reader slots available. "
57993                                 "You have to release some of your previously opened videos.",
57994                                 cimglist_instance,filename);
57995         cimg::mutex(9);
57996         captures[index] = cvCaptureFromFile(filename);
57997         CImg<charT>::string(filename).move_to(filenames[index]);
57998         positions[index] = 0;
57999         cimg::mutex(9,0);
58000         if (!captures[index]) {
58001           filenames[index].assign();
58002           std::fclose(cimg::fopen(filename,"rb"));  // Check file availability.
58003           throw CImgIOException(_cimglist_instance
58004                                 "load_video(): File '%s', unable to detect format of video file.",
58005                                 cimglist_instance,filename);
58006         }
58007       }
58008 
58009       cimg::mutex(9);
58010       const unsigned int nb_frames = (unsigned int)std::max(0.,cvGetCaptureProperty(captures[index],
58011                                                                                      CV_CAP_PROP_FRAME_COUNT));
58012       cimg::mutex(9,0);
58013       assign();
58014 
58015       // Skip frames if necessary.
58016       bool go_on = true;
58017       unsigned int &pos = positions[index];
58018       while (pos<first_frame) {
58019         cimg::mutex(9);
58020         if (!cvGrabFrame(captures[index])) { cimg::mutex(9,0); go_on = false; break; }
58021         cimg::mutex(9,0);
58022         ++pos;
58023       }
58024 
58025       // Read and convert frames.
58026       const IplImage *src = 0;
58027       if (go_on) {
58028         const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame);
58029         while (pos<=_last_frame) {
58030           cimg::mutex(9);
58031           src = cvQueryFrame(captures[index]);
58032           if (src) {
58033             CImg<T> frame(src->width,src->height,1,3);
58034             const int step = (int)(src->widthStep - 3*src->width);
58035             const unsigned char* ptrs = (unsigned char*)src->imageData;
58036             T *ptr_r = frame.data(0,0,0,0), *ptr_g = frame.data(0,0,0,1), *ptr_b = frame.data(0,0,0,2);
58037             if (step>0) cimg_forY(frame,y) {
58038                 cimg_forX(frame,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
58039                 ptrs+=step;
58040               } else for (ulongT siz = (ulongT)src->width*src->height; siz; --siz) {
58041                 *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++);
58042               }
58043             frame.move_to(*this);
58044             ++pos;
58045 
58046             bool skip_failed = false;
58047             for (unsigned int i = 1; i<step_frame && pos<=_last_frame; ++i, ++pos)
58048               if (!cvGrabFrame(captures[index])) { skip_failed = true; break; }
58049             if (skip_failed) src = 0;
58050           }
58051           cimg::mutex(9,0);
58052           if (!src) break;
58053         }
58054       }
58055 
58056       if (!src || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary.
58057         cimg::mutex(9);
58058         cvReleaseCapture(&captures[index]);
58059         captures[index] = 0;
58060         filenames[index].assign();
58061         positions[index] = 0;
58062         index = -1;
58063         cimg::mutex(9,0);
58064       }
58065 
58066       cimg::mutex(9);
58067       last_used_index = index;
58068       cimg::mutex(9,0);
58069 
58070       if (is_empty())
58071         throw CImgIOException(_cimglist_instance
58072                               "load_video(): File '%s', unable to locate frame %u.",
58073                               cimglist_instance,filename,first_frame);
58074       return *this;
58075 #endif
58076     }
58077 
58078     //! Load an image from a video file, using OpenCV library \newinstance.
58079     static CImgList<T> get_load_video(const char *const filename,
58080                            const unsigned int first_frame=0, const unsigned int last_frame=~0U,
58081                            const unsigned int step_frame=1) {
58082       return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame);
58083     }
58084 
58085     //! Load an image from a video file using the external tool 'ffmpeg'.
58086     /**
58087       \param filename Filename to read data from.
58088     **/
58089     CImgList<T>& load_ffmpeg_external(const char *const filename) {
58090       if (!filename)
58091         throw CImgArgumentException(_cimglist_instance
58092                                     "load_ffmpeg_external(): Specified filename is (null).",
58093                                     cimglist_instance);
58094       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
58095       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
58096       std::FILE *file = 0;
58097       do {
58098         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58099                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58100         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
58101         if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
58102       } while (file);
58103       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data);
58104       cimg_snprintf(command,command._width,"%s -i \"%s\" \"%s\"",
58105                     cimg::ffmpeg_path(),
58106                     CImg<charT>::string(filename)._system_strescape().data(),
58107                     CImg<charT>::string(filename_tmp2)._system_strescape().data());
58108       cimg::system(command,0);
58109       const unsigned int omode = cimg::exception_mode();
58110       cimg::exception_mode(0);
58111       assign();
58112       unsigned int i = 1;
58113       for (bool stop_flag = false; !stop_flag; ++i) {
58114         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i);
58115         CImg<T> img;
58116         try { img.load_pnm(filename_tmp2); }
58117         catch (CImgException&) { stop_flag = true; }
58118         if (img) { img.move_to(*this); std::remove(filename_tmp2); }
58119       }
58120       cimg::exception_mode(omode);
58121       if (is_empty())
58122         throw CImgIOException(_cimglist_instance
58123                               "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
58124                               cimglist_instance,
58125                               filename);
58126       return *this;
58127     }
58128 
58129     //! Load an image from a video file using the external tool 'ffmpeg' \newinstance.
58130     static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
58131       return CImgList<T>().load_ffmpeg_external(filename);
58132     }
58133 
58134     //! Load gif file, using ImageMagick or GraphicsMagick's external tools.
58135     /**
58136       \param filename Filename to read data from.
58137     **/
58138     CImgList<T>& load_gif_external(const char *const filename) {
58139       if (!filename)
58140         throw CImgArgumentException(_cimglist_instance
58141                                     "load_gif_external(): Specified filename is (null).",
58142                                     cimglist_instance);
58143       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
58144       if (!_load_gif_external(filename,false))
58145         if (!_load_gif_external(filename,true))
58146           try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); }
58147       if (is_empty())
58148         throw CImgIOException(_cimglist_instance
58149                               "load_gif_external(): Failed to open file '%s'.",
58150                               cimglist_instance,filename);
58151       return *this;
58152     }
58153 
58154     CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) {
58155       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
58156       std::FILE *file = 0;
58157       do {
58158         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58159                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58160         if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data);
58161         else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data);
58162         if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
58163       } while (file);
58164       if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"",
58165                                             cimg::graphicsmagick_path(),
58166                                             CImg<charT>::string(filename)._system_strescape().data(),
58167                                             CImg<charT>::string(filename_tmp)._system_strescape().data());
58168       else cimg_snprintf(command,command._width,"%s \"%s\" \"%s.png\"",
58169                          cimg::imagemagick_path(),
58170                          CImg<charT>::string(filename)._system_strescape().data(),
58171                          CImg<charT>::string(filename_tmp)._system_strescape().data());
58172       cimg::system(command,0);
58173       const unsigned int omode = cimg::exception_mode();
58174       cimg::exception_mode(0);
58175       assign();
58176 
58177       // Try to read a single frame gif.
58178       cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data);
58179       CImg<T> img;
58180       try { img.load_png(filename_tmp2); }
58181       catch (CImgException&) { }
58182       if (img) { img.move_to(*this); std::remove(filename_tmp2); }
58183       else { // Try to read animated gif.
58184         unsigned int i = 0;
58185         for (bool stop_flag = false; !stop_flag; ++i) {
58186           if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i);
58187           else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i);
58188           CImg<T> img;
58189           try { img.load_png(filename_tmp2); }
58190           catch (CImgException&) { stop_flag = true; }
58191           if (img) { img.move_to(*this); std::remove(filename_tmp2); }
58192         }
58193       }
58194       cimg::exception_mode(omode);
58195       return *this;
58196     }
58197 
58198     //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance.
58199     static CImgList<T> get_load_gif_external(const char *const filename) {
58200       return CImgList<T>().load_gif_external(filename);
58201     }
58202 
58203     //! Load a gzipped list, using external tool 'gunzip'.
58204     /**
58205       \param filename Filename to read data from.
58206     **/
58207     CImgList<T>& load_gzip_external(const char *const filename) {
58208       if (!filename)
58209         throw CImgIOException(_cimglist_instance
58210                               "load_gzip_external(): Specified filename is (null).",
58211                               cimglist_instance);
58212       std::fclose(cimg::fopen(filename,"rb"));            // Check if file exists.
58213       CImg<charT> command(1024), filename_tmp(256), body(256);
58214       const char
58215         *ext = cimg::split_filename(filename,body),
58216         *ext2 = cimg::split_filename(body,0);
58217       std::FILE *file = 0;
58218       do {
58219         if (!cimg::strcasecmp(ext,"gz")) {
58220           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58221                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
58222           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58223                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58224         } else {
58225           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
58226                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
58227           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58228                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58229         }
58230         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
58231       } while (file);
58232       cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
58233                     cimg::gunzip_path(),
58234                     CImg<charT>::string(filename)._system_strescape().data(),
58235                     CImg<charT>::string(filename_tmp)._system_strescape().data());
58236       cimg::system(command);
58237       if (!(file = std_fopen(filename_tmp,"rb"))) {
58238         cimg::fclose(cimg::fopen(filename,"r"));
58239         throw CImgIOException(_cimglist_instance
58240                               "load_gzip_external(): Failed to open file '%s'.",
58241                               cimglist_instance,
58242                               filename);
58243 
58244       } else cimg::fclose(file);
58245       load(filename_tmp);
58246       std::remove(filename_tmp);
58247       return *this;
58248     }
58249 
58250     //! Load a gzipped list, using external tool 'gunzip' \newinstance.
58251     static CImgList<T> get_load_gzip_external(const char *const filename) {
58252       return CImgList<T>().load_gzip_external(filename);
58253     }
58254 
58255     //! Load a 3d object from a .OFF file.
58256     /**
58257       \param filename Filename to read data from.
58258       \param[out] primitives At return, contains the list of 3d object primitives.
58259       \param[out] colors At return, contains the list of 3d object colors.
58260       \return List of 3d object vertices.
58261     **/
58262     template<typename tf, typename tc>
58263     CImgList<T>& load_off(const char *const filename,
58264 			  CImgList<tf>& primitives, CImgList<tc>& colors) {
58265       return get_load_off(filename,primitives,colors).move_to(*this);
58266     }
58267 
58268     //! Load a 3d object from a .OFF file \newinstance.
58269     template<typename tf, typename tc>
58270       static CImgList<T> get_load_off(const char *const filename,
58271                                       CImgList<tf>& primitives, CImgList<tc>& colors) {
58272       return CImg<T>().load_off(filename,primitives,colors)<'x';
58273     }
58274 
58275     //! Load images from a TIFF file.
58276     /**
58277         \param filename Filename to read data from.
58278         \param first_frame Index of first image frame to read.
58279         \param last_frame Index of last image frame to read.
58280         \param step_frame Step applied between each frame.
58281         \param[out] voxel_size Voxel size, as stored in the filename.
58282         \param[out] description Description, as stored in the filename.
58283     **/
58284     CImgList<T>& load_tiff(const char *const filename,
58285 			   const unsigned int first_frame=0, const unsigned int last_frame=~0U,
58286 			   const unsigned int step_frame=1,
58287                            float *const voxel_size=0,
58288                            CImg<charT> *const description=0) {
58289       const unsigned int
58290 	nfirst_frame = first_frame<last_frame?first_frame:last_frame,
58291 	nstep_frame = step_frame?step_frame:1;
58292       unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
58293 #ifndef cimg_use_tiff
58294       cimg::unused(voxel_size,description);
58295       if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
58296         throw CImgArgumentException(_cimglist_instance
58297                                     "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
58298                                     cimglist_instance,
58299                                     filename);
58300 
58301       return assign(CImg<T>::get_load_tiff(filename));
58302 #else
58303 #if cimg_verbosity<3
58304         TIFFSetWarningHandler(0);
58305         TIFFSetErrorHandler(0);
58306 #endif
58307       TIFF *tif = TIFFOpen(filename,"r");
58308       if (tif) {
58309         unsigned int nb_images = 0;
58310         do ++nb_images; while (TIFFReadDirectory(tif));
58311         if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
58312           cimg::warn(_cimglist_instance
58313                      "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since "
58314                      "file '%s' contains %u image(s).",
58315                      cimglist_instance,
58316                      nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
58317 
58318         if (nfirst_frame>=nb_images) return assign();
58319         if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
58320         assign(1 + (nlast_frame - nfirst_frame)/nstep_frame);
58321         TIFFSetDirectory(tif,0);
58322         cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,voxel_size,description);
58323         TIFFClose(tif);
58324       } else throw CImgIOException(_cimglist_instance
58325                                    "load_tiff(): Failed to open file '%s'.",
58326                                    cimglist_instance,
58327                                    filename);
58328       return *this;
58329 #endif
58330     }
58331 
58332     //! Load a multi-page TIFF file \newinstance.
58333     static CImgList<T> get_load_tiff(const char *const filename,
58334 				     const unsigned int first_frame=0, const unsigned int last_frame=~0U,
58335 				     const unsigned int step_frame=1,
58336                                      float *const voxel_size=0,
58337                                      CImg<charT> *const description=0) {
58338       return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame,voxel_size,description);
58339     }
58340 
58341     //@}
58342     //----------------------------------
58343     //
58344     //! \name Data Output
58345     //@{
58346     //----------------------------------
58347 
58348     //! Print information about the list on the standard output.
58349     /**
58350       \param title Label set to the information displayed.
58351       \param display_stats Tells if image statistics must be computed and displayed.
58352     **/
58353     const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
58354       unsigned int msiz = 0;
58355       cimglist_for(*this,l) msiz+=_data[l].size();
58356       msiz*=sizeof(T);
58357       const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U;
58358       CImg<charT> _title(64);
58359       if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type());
58360       std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
58361                    cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
58362                    cimg::t_bold,cimg::t_normal,(void*)this,
58363                    cimg::t_bold,cimg::t_normal,_width,_allocated_width,
58364                    mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
58365                    mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
58366                    cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
58367       if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1));
58368       else std::fprintf(cimg::output(),".\n");
58369 
58370       char tmp[16] = { 0 };
58371       cimglist_for(*this,ll) {
58372         cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
58373         std::fprintf(cimg::output(),"  ");
58374         _data[ll].print(tmp,display_stats);
58375         if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output(),"  ...\n"); }
58376       }
58377       std::fflush(cimg::output());
58378       return *this;
58379     }
58380 
58381     //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
58382     /**
58383        \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed.
58384        \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
58385        \param align Appending alignmenet.
58386        \note This function displays the list images of the current CImgList instance into an existing
58387          CImgDisplay window.
58388        Images of the list are appended in a single temporarly image for visualization purposes.
58389        The function returns immediately.
58390     **/
58391     const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
58392       disp.display(*this,axis,align);
58393       return *this;
58394     }
58395 
58396     //! Display the current CImgList instance in a new display window.
58397     /**
58398         \param disp Display window.
58399         \param display_info Tells if image information are displayed on the standard output.
58400         \param axis Alignment axis for images viewing.
58401         \param align Apending alignment.
58402         \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
58403         \param exit_on_anykey Exit function when any key is pressed.
58404         \note This function opens a new window with a specific title and displays the list images of the
58405           current CImgList instance into it.
58406         Images of the list are appended in a single temporarly image for visualization purposes.
58407         The function returns when a key is pressed or the display window is closed by the user.
58408     **/
58409     const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
58410                                const char axis='x', const float align=0,
58411                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
58412       bool is_exit = false;
58413       return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
58414     }
58415 
58416     //! Display the current CImgList instance in a new display window.
58417     /**
58418       \param title Title of the opening display window.
58419       \param display_info Tells if list information must be written on standard output.
58420       \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
58421       \param align Appending alignment.
58422       \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
58423       \param exit_on_anykey Exit function when any key is pressed.
58424     **/
58425     const CImgList<T>& display(const char *const title=0, const bool display_info=true,
58426                                const char axis='x', const float align=0,
58427                                unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
58428       CImgDisplay disp;
58429       bool is_exit = false;
58430       return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
58431     }
58432 
58433     const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const CImgList<charT> *const titles,
58434                                 const bool display_info, const char axis, const float align, unsigned int *const XYZ,
58435                                 const bool exit_on_anykey, const unsigned int orig, const bool is_first_call,
58436                                 bool &is_exit) const {
58437       if (is_empty())
58438         throw CImgInstanceException(_cimglist_instance
58439                                     "display(): Empty instance.",
58440                                     cimglist_instance);
58441       if (!disp) {
58442         if (axis=='x') {
58443           unsigned int sum_width = 0, max_height = 0;
58444           cimglist_for(*this,l) {
58445             const CImg<T> &img = _data[l];
58446             const unsigned int
58447               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
58448               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
58449             sum_width+=w;
58450             if (h>max_height) max_height = h;
58451           }
58452           disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1);
58453         } else {
58454           unsigned int max_width = 0, sum_height = 0;
58455           cimglist_for(*this,l) {
58456             const CImg<T> &img = _data[l];
58457             const unsigned int
58458               w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
58459               h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
58460             if (w>max_width) max_width = w;
58461             sum_height+=h;
58462           }
58463           disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1);
58464         }
58465         if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
58466       } else if (title) disp.set_title("%s",title);
58467       else if (titles) disp.set_title("%s",titles->__display()._data);
58468       const CImg<char> dtitle = CImg<char>::string(disp.title());
58469       if (display_info) print(disp.title());
58470       disp.show().flush();
58471 
58472       if (_width==1) {
58473         const unsigned int dw = disp._width, dh = disp._height;
58474         if (!is_first_call)
58475           disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false);
58476         disp.set_title("%s (%ux%ux%ux%u)",
58477                        dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
58478         _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call);
58479         if (disp.key()) is_exit = true;
58480         disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
58481       } else {
58482         bool disp_resize = !is_first_call;
58483         while (!disp.is_closed() && !is_exit) {
58484           const CImg<intT> s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true);
58485           disp_resize = true;
58486           if (s[0]<0 && !disp.wheel()) { // No selections done.
58487             if (disp.button()&2) { disp.flush(); break; }
58488             is_exit = true;
58489           } else if (disp.wheel()) { // Zoom in/out.
58490             const int wheel = disp.wheel();
58491             disp.set_wheel();
58492             if (!is_first_call && wheel<0) break;
58493             if (wheel>0 && _width>=4) {
58494               const unsigned int
58495                 delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)),
58496                 ind0 = (unsigned int)std::max(0,s[0] - (int)delta),
58497                 ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta);
58498               if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) {
58499                 const CImgList<T> sublist = get_shared_images(ind0,ind1);
58500                 CImgList<charT> t_sublist;
58501                 if (titles) t_sublist = titles->get_shared_images(ind0,ind1);
58502                 sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
58503                                  orig + ind0,false,is_exit);
58504               }
58505             }
58506           } else if (s[0]!=0 || s[1]!=width() - 1) {
58507             const CImgList<T> sublist = get_shared_images(s[0],s[1]);
58508             CImgList<charT> t_sublist;
58509             if (titles) t_sublist = titles->get_shared_images(s[0],s[1]);
58510             sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
58511                              orig + s[0],false,is_exit);
58512           }
58513           disp.set_title("%s",dtitle.data());
58514         }
58515       }
58516       return *this;
58517     }
58518 
58519     // [internal] Return string to describe display title.
58520     CImg<charT> __display() const {
58521       CImg<charT> res, str;
58522       cimglist_for(*this,l) {
58523         CImg<charT>::string(_data[l]).move_to(str);
58524         if (l!=width() - 1) {
58525           str.resize(str._width + 1,1,1,1,0);
58526           str[str._width - 2] = ',';
58527           str[str._width - 1] = ' ';
58528         }
58529         res.append(str,'x');
58530       }
58531       if (!res) return CImg<charT>(1,1,1,1,0).move_to(res);
58532       cimg::strellipsize(res,128,false);
58533       if (_width>1) {
58534         const unsigned int l = (unsigned int)std::strlen(res);
58535         if (res._width<=l + 16) res.resize(l + 16,1,1,1,0);
58536         cimg_snprintf(res._data + l,16," (#%u)",_width);
58537       }
58538       return res;
58539     }
58540 
58541     //! Save list into a file.
58542     /**
58543       \param filename Filename to write data to.
58544       \param number When positive, represents an index added to the filename. Otherwise, no number is added.
58545       \param digits Number of digits used for adding the number to the filename.
58546     **/
58547     const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
58548       if (!filename)
58549         throw CImgArgumentException(_cimglist_instance
58550                                     "save(): Specified filename is (null).",
58551                                     cimglist_instance);
58552       // Do not test for empty instances, since .cimg format is able to manage empty instances.
58553       const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
58554       const char *const ext = cimg::split_filename(filename);
58555       CImg<charT> nfilename(1024);
58556       const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename):
58557         filename;
58558 
58559 #ifdef cimglist_save_plugin
58560       cimglist_save_plugin(fn);
58561 #endif
58562 #ifdef cimglist_save_plugin1
58563       cimglist_save_plugin1(fn);
58564 #endif
58565 #ifdef cimglist_save_plugin2
58566       cimglist_save_plugin2(fn);
58567 #endif
58568 #ifdef cimglist_save_plugin3
58569       cimglist_save_plugin3(fn);
58570 #endif
58571 #ifdef cimglist_save_plugin4
58572       cimglist_save_plugin4(fn);
58573 #endif
58574 #ifdef cimglist_save_plugin5
58575       cimglist_save_plugin5(fn);
58576 #endif
58577 #ifdef cimglist_save_plugin6
58578       cimglist_save_plugin6(fn);
58579 #endif
58580 #ifdef cimglist_save_plugin7
58581       cimglist_save_plugin7(fn);
58582 #endif
58583 #ifdef cimglist_save_plugin8
58584       cimglist_save_plugin8(fn);
58585 #endif
58586       if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
58587       else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
58588       else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
58589       else if (!cimg::strcasecmp(ext,"avi") ||
58590                !cimg::strcasecmp(ext,"mov") ||
58591                !cimg::strcasecmp(ext,"asf") ||
58592                !cimg::strcasecmp(ext,"divx") ||
58593                !cimg::strcasecmp(ext,"flv") ||
58594                !cimg::strcasecmp(ext,"mpg") ||
58595                !cimg::strcasecmp(ext,"m1v") ||
58596                !cimg::strcasecmp(ext,"m2v") ||
58597                !cimg::strcasecmp(ext,"m4v") ||
58598                !cimg::strcasecmp(ext,"mjp") ||
58599                !cimg::strcasecmp(ext,"mp4") ||
58600                !cimg::strcasecmp(ext,"mkv") ||
58601                !cimg::strcasecmp(ext,"mpe") ||
58602                !cimg::strcasecmp(ext,"movie") ||
58603                !cimg::strcasecmp(ext,"ogm") ||
58604                !cimg::strcasecmp(ext,"ogg") ||
58605                !cimg::strcasecmp(ext,"ogv") ||
58606                !cimg::strcasecmp(ext,"qt") ||
58607                !cimg::strcasecmp(ext,"rm") ||
58608                !cimg::strcasecmp(ext,"vob") ||
58609                !cimg::strcasecmp(ext,"wmv") ||
58610                !cimg::strcasecmp(ext,"xvid") ||
58611                !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
58612 #ifdef cimg_use_tiff
58613       else if (!cimg::strcasecmp(ext,"tif") ||
58614           !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
58615 #endif
58616       else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
58617       else {
58618         if (_width==1) _data[0].save(fn,-1);
58619         else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); }
58620       }
58621       return *this;
58622     }
58623 
58624     //! Tell if an image list can be saved as one single file.
58625     /**
58626        \param filename Filename, as a C-string.
58627        \return \c true if the file format supports multiple images, \c false otherwise.
58628     **/
58629     static bool is_saveable(const char *const filename) {
58630       const char *const ext = cimg::split_filename(filename);
58631       if (!cimg::strcasecmp(ext,"cimgz") ||
58632 #ifdef cimg_use_tiff
58633           !cimg::strcasecmp(ext,"tif") ||
58634           !cimg::strcasecmp(ext,"tiff") ||
58635 #endif
58636           !cimg::strcasecmp(ext,"yuv") ||
58637           !cimg::strcasecmp(ext,"avi") ||
58638           !cimg::strcasecmp(ext,"mov") ||
58639           !cimg::strcasecmp(ext,"asf") ||
58640           !cimg::strcasecmp(ext,"divx") ||
58641           !cimg::strcasecmp(ext,"flv") ||
58642           !cimg::strcasecmp(ext,"mpg") ||
58643           !cimg::strcasecmp(ext,"m1v") ||
58644           !cimg::strcasecmp(ext,"m2v") ||
58645           !cimg::strcasecmp(ext,"m4v") ||
58646           !cimg::strcasecmp(ext,"mjp") ||
58647           !cimg::strcasecmp(ext,"mp4") ||
58648           !cimg::strcasecmp(ext,"mkv") ||
58649           !cimg::strcasecmp(ext,"mpe") ||
58650           !cimg::strcasecmp(ext,"movie") ||
58651           !cimg::strcasecmp(ext,"ogm") ||
58652           !cimg::strcasecmp(ext,"ogg") ||
58653           !cimg::strcasecmp(ext,"ogv") ||
58654           !cimg::strcasecmp(ext,"qt") ||
58655           !cimg::strcasecmp(ext,"rm") ||
58656           !cimg::strcasecmp(ext,"vob") ||
58657           !cimg::strcasecmp(ext,"wmv") ||
58658           !cimg::strcasecmp(ext,"xvid") ||
58659           !cimg::strcasecmp(ext,"mpeg")) return true;
58660       return false;
58661     }
58662 
58663     //! Save image sequence as a GIF animated file.
58664     /**
58665        \param filename Filename to write data to.
58666        \param fps Number of desired frames per second.
58667        \param nb_loops Number of loops (\c 0 for infinite looping).
58668     **/
58669     const CImgList<T>& save_gif_external(const char *const filename, const float fps=25,
58670                                          const unsigned int nb_loops=0) {
58671       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
58672       CImgList<charT> filenames;
58673       std::FILE *file = 0;
58674 
58675 #ifdef cimg_use_png
58676 #define _cimg_save_gif_ext "png"
58677 #else
58678 #define _cimg_save_gif_ext "ppm"
58679 #endif
58680 
58681       do {
58682         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
58683                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
58684         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_ext,filename_tmp._data);
58685         if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
58686       } while (file);
58687       cimglist_for(*this,l) {
58688         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_ext,filename_tmp._data,l + 1);
58689         CImg<charT>::string(filename_tmp2).move_to(filenames);
58690         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2);
58691         else _data[l].save(filename_tmp2);
58692       }
58693       cimg_snprintf(command,command._width,"%s -delay %u -loop %u",
58694                     cimg::imagemagick_path(),(unsigned int)std::max(0.0f,cimg::round(100/fps)),nb_loops);
58695       CImg<ucharT>::string(command).move_to(filenames,0);
58696       cimg_snprintf(command,command._width,"\"%s\"",
58697                     CImg<charT>::string(filename)._system_strescape().data());
58698       CImg<ucharT>::string(command).move_to(filenames);
58699       CImg<charT> _command = filenames>'x';
58700       cimg_for(_command,p,char) if (!*p) *p = ' ';
58701       _command.back() = 0;
58702 
58703       cimg::system(_command);
58704       file = std_fopen(filename,"rb");
58705       if (!file)
58706         throw CImgIOException(_cimglist_instance
58707                               "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.",
58708                               cimglist_instance,
58709                               filename);
58710       else cimg::fclose(file);
58711       cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]);
58712       return *this;
58713     }
58714 
58715     //! Save list as a YUV image sequence file.
58716     /**
58717       \param filename Filename to write data to.
58718       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
58719       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
58720     **/
58721     const CImgList<T>& save_yuv(const char *const filename=0,
58722                                 const unsigned int chroma_subsampling=444,
58723                                 const bool is_rgb=true) const {
58724       return _save_yuv(0,filename,chroma_subsampling,is_rgb);
58725     }
58726 
58727     //! Save image sequence into a YUV file.
58728     /**
58729       \param file File to write data to.
58730       \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
58731       \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
58732     **/
58733     const CImgList<T>& save_yuv(std::FILE *const file,
58734                                 const unsigned int chroma_subsampling=444,
58735                                 const bool is_rgb=true) const {
58736       return _save_yuv(file,0,chroma_subsampling,is_rgb);
58737     }
58738 
58739     const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename,
58740                                  const unsigned int chroma_subsampling,
58741                                  const bool is_rgb) const {
58742       if (!file && !filename)
58743         throw CImgArgumentException(_cimglist_instance
58744                                     "save_yuv(): Specified filename is (null).",
58745                                     cimglist_instance);
58746       if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
58747         throw CImgArgumentException(_cimglist_instance
58748                                     "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
58749                                     cimglist_instance,
58750                                     chroma_subsampling,filename?filename:"(FILE*)");
58751       if (is_empty()) { cimg::fempty(file,filename); return *this; }
58752       const unsigned int
58753         cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
58754         cfy = chroma_subsampling==420?2:1,
58755         w0 = (*this)[0]._width, h0 = (*this)[0]._height,
58756         width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy);
58757       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58758       cimglist_for(*this,l) {
58759         const CImg<T> &frame = (*this)[l];
58760         cimg_forZ(frame,z) {
58761           CImg<ucharT> YUV;
58762           if (sizeof(T)==1 && !is_rgb &&
58763               frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3)
58764             YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true);
58765           else {
58766             YUV = frame.get_slice(z);
58767             if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0);
58768             if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0);
58769             if (is_rgb) YUV.RGBtoYCbCr();
58770           }
58771           if (chroma_subsampling==444)
58772             cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile);
58773           else {
58774             cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile);
58775             CImg<ucharT> UV = YUV.get_channels(1,2);
58776             UV.resize(UV._width/cfx,UV._height/cfy,1,2,2);
58777             cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile);
58778           }
58779         }
58780       }
58781       if (!file) cimg::fclose(nfile);
58782       return *this;
58783     }
58784 
58785     //! Save list into a .cimg file.
58786     /**
58787        \param filename Filename to write data to.
58788        \param is_compressed Tells if data compression must be enabled.
58789     **/
58790     const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
58791       return _save_cimg(0,filename,is_compressed);
58792     }
58793 
58794     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
58795       if (!file && !filename)
58796         throw CImgArgumentException(_cimglist_instance
58797                                     "save_cimg(): Specified filename is (null).",
58798                                     cimglist_instance);
58799 #ifndef cimg_use_zlib
58800       if (is_compressed)
58801         cimg::warn(_cimglist_instance
58802                    "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
58803                    "saving them uncompressed.",
58804                    cimglist_instance,
58805                    filename?filename:"(FILE*)");
58806 #endif
58807       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
58808       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
58809       if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
58810       else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
58811       cimglist_for(*this,l) {
58812         const CImg<T>& img = _data[l];
58813         std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
58814         if (img._data) {
58815           CImg<T> tmp;
58816           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
58817           const CImg<T>& ref = cimg::endianness()?tmp:img;
58818           bool failed_to_compress = true;
58819           if (is_compressed) {
58820 #ifdef cimg_use_zlib
58821             const ulongT siz = sizeof(T)*ref.size();
58822             uLongf csiz = siz + siz/100 + 16;
58823             Bytef *const cbuf = new Bytef[csiz];
58824             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
58825               cimg::warn(_cimglist_instance
58826                          "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
58827                          cimglist_instance,
58828                          filename?filename:"(FILE*)");
58829             else {
58830               std::fprintf(nfile," #%lu\n",csiz);
58831               cimg::fwrite(cbuf,csiz,nfile);
58832               delete[] cbuf;
58833               failed_to_compress = false;
58834             }
58835 #endif
58836           }
58837           if (failed_to_compress) { // Write in a non-compressed way.
58838             std::fputc('\n',nfile);
58839             cimg::fwrite(ref._data,ref.size(),nfile);
58840           }
58841         } else std::fputc('\n',nfile);
58842       }
58843       if (!file) cimg::fclose(nfile);
58844       return *this;
58845     }
58846 
58847     //! Save list into a .cimg file.
58848     /**
58849        \param file File to write data to.
58850        \param is_compressed Tells if data compression must be enabled.
58851     **/
58852     const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
58853       return _save_cimg(file,0,is_compressed);
58854     }
58855 
58856     const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
58857                                  const unsigned int n0,
58858                                  const unsigned int x0, const unsigned int y0,
58859                                  const unsigned int z0, const unsigned int c0) const {
58860 #define _cimg_save_cimg_case(Ts,Tss) \
58861       if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
58862         for (unsigned int l = 0; l<lmax; ++l) { \
58863           j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
58864           W = H = D = C = 0; \
58865           if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
58866             throw CImgIOException(_cimglist_instance \
58867                                   "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
58868                                   cimglist_instance, \
58869                                   W,H,D,C,l,filename?filename:"(FILE*)"); \
58870           if (W*H*D*C>0) { \
58871             if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
58872             else { \
58873               const CImg<T>& img = (*this)[l - n0]; \
58874               const T *ptrs = img._data; \
58875               const unsigned int \
58876                 x1 = x0 + img._width - 1, \
58877                 y1 = y0 + img._height - 1, \
58878                 z1 = z0 + img._depth - 1, \
58879                 c1 = c0 + img._spectrum - 1, \
58880                 nx1 = x1>=W?W - 1:x1, \
58881                 ny1 = y1>=H?H - 1:y1, \
58882                 nz1 = z1>=D?D - 1:z1, \
58883                 nc1 = c1>=C?C - 1:c1; \
58884               CImg<Tss> raw(1 + nx1 - x0); \
58885               const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
58886               if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
58887               for (unsigned int v = 1 + nc1 - c0; v; --v) { \
58888                 const unsigned int skipzb = z0*W*H*sizeof(Tss); \
58889                 if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
58890                 for (unsigned int z = 1 + nz1 - z0; z; --z) { \
58891                   const unsigned int skipyb = y0*W*sizeof(Tss); \
58892                   if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
58893                   for (unsigned int y = 1 + ny1 - y0; y; --y) { \
58894                     const unsigned int skipxb = x0*sizeof(Tss); \
58895                     if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
58896                     raw.assign(ptrs, raw._width); \
58897                     ptrs+=img._width; \
58898                     if (endian) cimg::invert_endianness(raw._data,raw._width); \
58899                     cimg::fwrite(raw._data,raw._width,nfile); \
58900                     const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
58901                     if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
58902                   } \
58903                   const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
58904                   if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
58905                 } \
58906                 const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
58907                 if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
58908               } \
58909               const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
58910               if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
58911             } \
58912           } \
58913         } \
58914         saved = true; \
58915       }
58916 
58917       if (!file && !filename)
58918         throw CImgArgumentException(_cimglist_instance
58919                                     "save_cimg(): Specified filename is (null).",
58920                                     cimglist_instance);
58921       if (is_empty())
58922         throw CImgInstanceException(_cimglist_instance
58923                                     "save_cimg(): Empty instance, for file '%s'.",
58924                                     cimglist_instance,
58925                                     filename?filename:"(FILE*)");
58926 
58927       std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
58928       bool saved = false, endian = cimg::endianness();
58929       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
58930       *tmp = *str_pixeltype = *str_endian = 0;
58931       unsigned int j, N, W, H, D, C;
58932       int i, err;
58933       j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
58934       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data);
58935       if (err<2) {
58936         if (!file) cimg::fclose(nfile);
58937         throw CImgIOException(_cimglist_instance
58938                               "save_cimg(): CImg header not found in file '%s'.",
58939                               cimglist_instance,
58940                               filename?filename:"(FILE*)");
58941       }
58942       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
58943       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
58944       const unsigned int lmax = std::min(N,n0 + _width);
58945       _cimg_save_cimg_case("bool",bool);
58946       _cimg_save_cimg_case("unsigned_char",unsigned char);
58947       _cimg_save_cimg_case("uchar",unsigned char);
58948       _cimg_save_cimg_case("char",char);
58949       _cimg_save_cimg_case("unsigned_short",unsigned short);
58950       _cimg_save_cimg_case("ushort",unsigned short);
58951       _cimg_save_cimg_case("short",short);
58952       _cimg_save_cimg_case("unsigned_int",unsigned int);
58953       _cimg_save_cimg_case("uint",unsigned int);
58954       _cimg_save_cimg_case("int",int);
58955       _cimg_save_cimg_case("unsigned_int64",uint64T);
58956       _cimg_save_cimg_case("uint64",uint64T);
58957       _cimg_save_cimg_case("int64",int64T);
58958       _cimg_save_cimg_case("float",float);
58959       _cimg_save_cimg_case("double",double);
58960       if (!saved) {
58961         if (!file) cimg::fclose(nfile);
58962         throw CImgIOException(_cimglist_instance
58963                               "save_cimg(): Unsupported data type '%s' for file '%s'.",
58964                               cimglist_instance,
58965                               filename?filename:"(FILE*)",str_pixeltype._data);
58966       }
58967       if (!file) cimg::fclose(nfile);
58968       return *this;
58969     }
58970 
58971     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
58972     /**
58973       \param filename Filename to write data to.
58974       \param n0 Starting index of images to write.
58975       \param x0 Starting X-coordinates of image regions to write.
58976       \param y0 Starting Y-coordinates of image regions to write.
58977       \param z0 Starting Z-coordinates of image regions to write.
58978       \param c0 Starting C-coordinates of image regions to write.
58979     **/
58980     const CImgList<T>& save_cimg(const char *const filename,
58981                                  const unsigned int n0,
58982                                  const unsigned int x0, const unsigned int y0,
58983                                  const unsigned int z0, const unsigned int c0) const {
58984       return _save_cimg(0,filename,n0,x0,y0,z0,c0);
58985     }
58986 
58987     //! Insert the image instance into into an existing .cimg file, at specified coordinates.
58988     /**
58989       \param file File to write data to.
58990       \param n0 Starting index of images to write.
58991       \param x0 Starting X-coordinates of image regions to write.
58992       \param y0 Starting Y-coordinates of image regions to write.
58993       \param z0 Starting Z-coordinates of image regions to write.
58994       \param c0 Starting C-coordinates of image regions to write.
58995     **/
58996     const CImgList<T>& save_cimg(std::FILE *const file,
58997                                  const unsigned int n0,
58998                                  const unsigned int x0, const unsigned int y0,
58999                                  const unsigned int z0, const unsigned int c0) const {
59000       return _save_cimg(file,0,n0,x0,y0,z0,c0);
59001     }
59002 
59003     static void _save_empty_cimg(std::FILE *const file, const char *const filename,
59004                                 const unsigned int nb,
59005                                 const unsigned int dx, const unsigned int dy,
59006                                 const unsigned int dz, const unsigned int dc) {
59007       std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
59008       const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T);
59009       std::fprintf(nfile,"%u %s\n",nb,pixel_type());
59010       for (unsigned int i=nb; i; --i) {
59011         std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
59012 	for (ulongT off = siz; off; --off) std::fputc(0,nfile);
59013       }
59014       if (!file) cimg::fclose(nfile);
59015     }
59016 
59017     //! Save empty (non-compressed) .cimg file with specified dimensions.
59018     /**
59019         \param filename Filename to write data to.
59020         \param nb Number of images to write.
59021         \param dx Width of images in the written file.
59022         \param dy Height of images in the written file.
59023         \param dz Depth of images in the written file.
59024         \param dc Spectrum of images in the written file.
59025     **/
59026     static void save_empty_cimg(const char *const filename,
59027                                 const unsigned int nb,
59028                                 const unsigned int dx, const unsigned int dy=1,
59029                                 const unsigned int dz=1, const unsigned int dc=1) {
59030       return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
59031     }
59032 
59033     //! Save empty .cimg file with specified dimensions.
59034     /**
59035         \param file File to write data to.
59036         \param nb Number of images to write.
59037         \param dx Width of images in the written file.
59038         \param dy Height of images in the written file.
59039         \param dz Depth of images in the written file.
59040         \param dc Spectrum of images in the written file.
59041     **/
59042     static void save_empty_cimg(std::FILE *const file,
59043                                 const unsigned int nb,
59044                                 const unsigned int dx, const unsigned int dy=1,
59045                                 const unsigned int dz=1, const unsigned int dc=1) {
59046       return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
59047     }
59048 
59049     //! Save list as a TIFF file.
59050     /**
59051       \param filename Filename to write data to.
59052       \param compression_type Compression mode used to write data.
59053       \param voxel_size Voxel size, to be stored in the filename.
59054       \param description Description, to be stored in the filename.
59055       \param use_bigtiff Allow to save big tiff files (>4Gb).
59056     **/
59057     const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
59058                                  const float *const voxel_size=0, const char *const description=0,
59059                                  const bool use_bigtiff=true) const {
59060       if (!filename)
59061         throw CImgArgumentException(_cimglist_instance
59062                                     "save_tiff(): Specified filename is (null).",
59063                                     cimglist_instance);
59064       if (is_empty()) { cimg::fempty(0,filename); return *this; }
59065 
59066 #ifndef cimg_use_tiff
59067       if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff);
59068       else cimglist_for(*this,l) {
59069           CImg<charT> nfilename(1024);
59070           cimg::number_filename(filename,l,6,nfilename);
59071           _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff);
59072         }
59073 #else
59074       ulongT siz = 0;
59075       cimglist_for(*this,l) siz+=_data[l].size();
59076       const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images.
59077       TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
59078       if (tif) {
59079         for (unsigned int dir = 0, l = 0; l<_width; ++l) {
59080           const CImg<T>& img = (*this)[l];
59081           cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description);
59082         }
59083         TIFFClose(tif);
59084       } else
59085         throw CImgIOException(_cimglist_instance
59086                               "save_tiff(): Failed to open stream for file '%s'.",
59087                               cimglist_instance,
59088                               filename);
59089 #endif
59090       return *this;
59091     }
59092 
59093     //! Save list as a gzipped file, using external tool 'gzip'.
59094     /**
59095       \param filename Filename to write data to.
59096     **/
59097     const CImgList<T>& save_gzip_external(const char *const filename) const {
59098       if (!filename)
59099         throw CImgIOException(_cimglist_instance
59100                               "save_gzip_external(): Specified filename is (null).",
59101                               cimglist_instance);
59102       CImg<charT> command(1024), filename_tmp(256), body(256);
59103       const char
59104         *ext = cimg::split_filename(filename,body),
59105         *ext2 = cimg::split_filename(body,0);
59106       std::FILE *file;
59107       do {
59108         if (!cimg::strcasecmp(ext,"gz")) {
59109           if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
59110                                    cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
59111           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
59112                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
59113         } else {
59114           if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
59115                                   cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
59116           else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
59117                              cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
59118         }
59119         if ((file=std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
59120       } while (file);
59121 
59122       if (is_saveable(body)) {
59123         save(filename_tmp);
59124         cimg_snprintf(command,command._width,"%s -c \"%s\" > \"%s\"",
59125                       cimg::gzip_path(),
59126                       CImg<charT>::string(filename_tmp)._system_strescape().data(),
59127                       CImg<charT>::string(filename)._system_strescape().data());
59128         cimg::system(command);
59129         file = std_fopen(filename,"rb");
59130         if (!file)
59131           throw CImgIOException(_cimglist_instance
59132                                 "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
59133                                 cimglist_instance,
59134                                 filename);
59135         else cimg::fclose(file);
59136         std::remove(filename_tmp);
59137       } else {
59138         CImg<charT> nfilename(1024);
59139         cimglist_for(*this,l) {
59140           cimg::number_filename(body,l,6,nfilename);
59141           if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext);
59142           _data[l].save_gzip_external(nfilename);
59143         }
59144       }
59145       return *this;
59146     }
59147 
59148     //! Save image sequence, using the OpenCV library.
59149     /**
59150        \param filename Filename to write data to.
59151        \param fps Number of frames per second.
59152        \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
59153        \param keep_open Tells if the video writer associated to the specified filename
59154        must be kept open or not (to allow frames to be added in the same file afterwards).
59155     **/
59156     const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25,
59157                                   const char *codec=0, const bool keep_open=false) const {
59158 #ifndef cimg_use_opencv
59159       cimg::unused(codec,keep_open);
59160       return save_ffmpeg_external(filename,fps);
59161 #else
59162       static CvVideoWriter *writers[32] = { 0 };
59163       static CImgList<charT> filenames(32);
59164       static CImg<intT> sizes(32,2,1,1,0);
59165       static int last_used_index = -1;
59166 
59167       // Detect if a video writer already exists for the specified filename.
59168       cimg::mutex(9);
59169       int index = -1;
59170       if (filename) {
59171         if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
59172           index = last_used_index;
59173         } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
59174             index = l; break;
59175           }
59176       } else index = last_used_index;
59177       cimg::mutex(9,0);
59178 
59179       // Find empty slot for capturing video stream.
59180       if (index<0) {
59181         if (!filename)
59182           throw CImgArgumentException(_cimglist_instance
59183                                       "save_video(): No already open video writer found. You must specify a "
59184                                       "non-(null) filename argument for the first call.",
59185                                       cimglist_instance);
59186         else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
59187         if (index<0)
59188           throw CImgIOException(_cimglist_instance
59189                                 "save_video(): File '%s', no video writer slots available. "
59190                                 "You have to release some of your previously opened videos.",
59191                                 cimglist_instance,filename);
59192         if (is_empty())
59193           throw CImgInstanceException(_cimglist_instance
59194                                       "save_video(): Instance list is empty.",
59195                                       cimglist_instance);
59196         const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0;
59197         if (!W || !H)
59198           throw CImgInstanceException(_cimglist_instance
59199                                       "save_video(): Frame [0] is an empty image.",
59200                                       cimglist_instance);
59201 
59202 #define _cimg_docase(x) ((x)>='a'&&(x)<='z'?(x) + 'A' - 'a':(x))
59203 
59204         const char
59205           *const _codec = codec && *codec?codec:cimg_OS==2?"mpeg":"mp4v",
59206           codec0 = _cimg_docase(_codec[0]),
59207           codec1 = _codec[0]?_cimg_docase(_codec[1]):0,
59208           codec2 = _codec[1]?_cimg_docase(_codec[2]):0,
59209           codec3 = _codec[2]?_cimg_docase(_codec[3]):0;
59210         cimg::mutex(9);
59211         writers[index] = cvCreateVideoWriter(filename,CV_FOURCC(codec0,codec1,codec2,codec3),
59212                                              fps,cvSize(W,H));
59213         CImg<charT>::string(filename).move_to(filenames[index]);
59214         sizes(index,0) = W; sizes(index,1) = H;
59215         cimg::mutex(9,0);
59216         if (!writers[index])
59217           throw CImgIOException(_cimglist_instance
59218                                 "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.",
59219                                 cimglist_instance,filename,
59220                                 codec0,codec1,codec2,codec3);
59221       }
59222 
59223       if (!is_empty()) {
59224         const unsigned int W = sizes(index,0), H = sizes(index,1);
59225         cimg::mutex(9);
59226         IplImage *ipl = cvCreateImage(cvSize(W,H),8,3);
59227         cimglist_for(*this,l) {
59228           CImg<T> &src = _data[l];
59229           if (src.is_empty())
59230             cimg::warn(_cimglist_instance
59231                        "save_video(): Skip empty frame %d for file '%s'.",
59232                        cimglist_instance,l,filename);
59233           if (src._depth>1 || src._spectrum>3)
59234             cimg::warn(_cimglist_instance
59235                        "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). "
59236                        "Some image data may be ignored when writing frame into video file '%s'.",
59237                        cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename);
59238           if (src._width==W && src._height==H && src._spectrum==3) {
59239             const T *ptr_r = src.data(0,0,0,0), *ptr_g = src.data(0,0,0,1), *ptr_b = src.data(0,0,0,2);
59240             char *ptrd = ipl->imageData;
59241             cimg_forXY(src,x,y) {
59242               *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++);
59243             }
59244           } else {
59245             CImg<unsigned char> _src(src,false);
59246             _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H);
59247             _src.resize(W,H,1,3,_src._spectrum==1);
59248             const unsigned char *ptr_r = _src.data(0,0,0,0), *ptr_g = _src.data(0,0,0,1), *ptr_b = _src.data(0,0,0,2);
59249             char *ptrd = ipl->imageData;
59250             cimg_forXY(_src,x,y) {
59251               *(ptrd++) = (char)*(ptr_b++); *(ptrd++) = (char)*(ptr_g++); *(ptrd++) = (char)*(ptr_r++);
59252             }
59253           }
59254           cvWriteFrame(writers[index],ipl);
59255         }
59256         cvReleaseImage(&ipl);
59257         cimg::mutex(9,0);
59258       }
59259 
59260       cimg::mutex(9);
59261       if (!keep_open) {
59262         cvReleaseVideoWriter(&writers[index]);
59263         writers[index] = 0;
59264         filenames[index].assign();
59265         sizes(index,0) = sizes(index,1) = 0;
59266         last_used_index = -1;
59267       } else last_used_index = index;
59268       cimg::mutex(9,0);
59269 
59270       return *this;
59271 #endif
59272     }
59273 
59274     //! Save image sequence, using the external tool 'ffmpeg'.
59275     /**
59276       \param filename Filename to write data to.
59277       \param fps Number of frames per second.
59278       \param codec Type of compression.
59279       \param bitrate Output bitrate
59280     **/
59281     const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
59282                                             const char *const codec=0, const unsigned int bitrate=2048) const {
59283       if (!filename)
59284         throw CImgArgumentException(_cimglist_instance
59285                                     "save_ffmpeg_external(): Specified filename is (null).",
59286                                     cimglist_instance);
59287       if (is_empty()) { cimg::fempty(0,filename); return *this; }
59288 
59289       const char
59290         *const ext = cimg::split_filename(filename),
59291         *const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video";
59292 
59293       CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
59294       CImgList<charT> filenames;
59295       std::FILE *file = 0;
59296       cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
59297         throw CImgInstanceException(_cimglist_instance
59298                                     "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
59299                                     cimglist_instance,
59300                                     filename);
59301       do {
59302         cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
59303                       cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
59304         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
59305         if ((file=std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
59306       } while (file);
59307       cimglist_for(*this,l) {
59308         cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1);
59309         CImg<charT>::string(filename_tmp2).move_to(filenames);
59310         if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filename_tmp2);
59311         else _data[l].save_pnm(filename_tmp2);
59312       }
59313       cimg_snprintf(command,command._width,"%s -i \"%s_%%6d.ppm\" -vcodec %s -b %uk -r %u -y \"%s\"",
59314                     cimg::ffmpeg_path(),
59315                     CImg<charT>::string(filename_tmp)._system_strescape().data(),
59316                     _codec,bitrate,fps,
59317                     CImg<charT>::string(filename)._system_strescape().data());
59318       cimg::system(command);
59319       file = std_fopen(filename,"rb");
59320       if (!file)
59321         throw CImgIOException(_cimglist_instance
59322                               "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
59323                               cimglist_instance,
59324                               filename);
59325       else cimg::fclose(file);
59326       cimglist_for(*this,l) std::remove(filenames[l]);
59327       return *this;
59328     }
59329 
59330     //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
59331     /**
59332        \param is_compressed tells if zlib compression must be used for serialization
59333        (this requires 'cimg_use_zlib' been enabled).
59334     **/
59335     CImg<ucharT> get_serialize(const bool is_compressed=false) const {
59336 #ifndef cimg_use_zlib
59337       if (is_compressed)
59338         cimg::warn(_cimglist_instance
59339                    "get_serialize(): Unable to compress data unless zlib is enabled, "
59340                    "storing them uncompressed.",
59341                    cimglist_instance);
59342 #endif
59343       CImgList<ucharT> stream;
59344       CImg<charT> tmpstr(128);
59345       const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
59346       if (std::strstr(ptype,"unsigned")==ptype)
59347         cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype);
59348       else
59349         cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype);
59350       CImg<ucharT>::string(tmpstr,false).move_to(stream);
59351       cimglist_for(*this,l) {
59352         const CImg<T>& img = _data[l];
59353         cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
59354         CImg<ucharT>::string(tmpstr,false).move_to(stream);
59355         if (img._data) {
59356           CImg<T> tmp;
59357           if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
59358           const CImg<T>& ref = cimg::endianness()?tmp:img;
59359           bool failed_to_compress = true;
59360           if (is_compressed) {
59361 #ifdef cimg_use_zlib
59362             const ulongT siz = sizeof(T)*ref.size();
59363             uLongf csiz = (ulongT)compressBound(siz);
59364             Bytef *const cbuf = new Bytef[csiz];
59365             if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
59366               cimg::warn(_cimglist_instance
59367                          "get_serialize(): Failed to save compressed data, saving them uncompressed.",
59368                          cimglist_instance);
59369             else {
59370               cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz);
59371               CImg<ucharT>::string(tmpstr,false).move_to(stream);
59372               CImg<ucharT>(cbuf,csiz).move_to(stream);
59373               delete[] cbuf;
59374               failed_to_compress = false;
59375             }
59376 #endif
59377           }
59378           if (failed_to_compress) { // Write in a non-compressed way.
59379             CImg<charT>::string("\n",false).move_to(stream);
59380             stream.insert(1);
59381             stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true);
59382           }
59383         } else CImg<charT>::string("\n",false).move_to(stream);
59384       }
59385       cimglist_apply(stream,unroll)('y');
59386       return stream>'y';
59387     }
59388 
59389     //! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list.
59390     template<typename t>
59391     static CImgList<T> get_unserialize(const CImg<t>& buffer) {
59392 #ifdef cimg_use_zlib
59393 #define _cimgz_unserialize_case(Tss) { \
59394         Bytef *cbuf = 0; \
59395         if (sizeof(t)!=1 || cimg::type<t>::string()==cimg::type<bool>::string()) { \
59396           cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \
59397           for (ulongT i = 0; i<csiz; ++i) *(_cbuf++) = (Bytef)*(stream++); \
59398           is_bytef = false; \
59399         } else { cbuf = (Bytef*)stream; stream+=csiz; is_bytef = true; } \
59400         raw.assign(W,H,D,C); \
59401         uLongf destlen = raw.size()*sizeof(Tss); \
59402         uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
59403         if (!is_bytef) delete[] cbuf; \
59404       }
59405 #else
59406 #define _cimgz_unserialize_case(Tss) \
59407       throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \
59408                                   "unless zlib is enabled.", \
59409                                   pixel_type());
59410 #endif
59411 
59412 #define _cimg_unserialize_case(Ts,Tss) \
59413       if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
59414         for (unsigned int l = 0; l<N; ++l) { \
59415           j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \
59416           ++stream; tmp[j] = 0; \
59417           W = H = D = C = 0; csiz = 0; \
59418           if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
59419             throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \
59420                                         "image #%u in serialized buffer.", \
59421                                         pixel_type(),W,H,D,C,l); \
59422           if (W*H*D*C>0) { \
59423             CImg<Tss> raw; \
59424             CImg<T> &img = res._data[l]; \
59425             if (err==5) _cimgz_unserialize_case(Tss) \
59426             else if (sizeof(Tss)==sizeof(t) && cimg::type<Tss>::is_float()==cimg::type<t>::is_float()) { \
59427               raw.assign((Tss*)stream,W,H,D,C,true); \
59428               stream+=raw.size(); \
59429             } else { \
59430               raw.assign(W,H,D,C); \
59431               CImg<ucharT> _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \
59432               cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \
59433             } \
59434             if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
59435             raw.move_to(img); \
59436           } \
59437         } \
59438         loaded = true; \
59439       }
59440 
59441       if (buffer.is_empty())
59442         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).",
59443                                     pixel_type());
59444       CImgList<T> res;
59445       const t *stream = buffer._data, *const estream = buffer._data + buffer.size();
59446       bool loaded = false, endian = cimg::endianness(), is_bytef = false;
59447       CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
59448       *tmp = *str_pixeltype = *str_endian = 0;
59449       unsigned int j, N = 0, W, H, D, C;
59450       uint64T csiz;
59451       int i, err;
59452       cimg::unused(is_bytef);
59453       do {
59454         j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; }
59455         ++stream; tmp[j] = 0;
59456       } while (*tmp=='#' && stream<estream);
59457       err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",
59458                         &N,str_pixeltype._data,str_endian._data);
59459       if (err<2)
59460         throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.",
59461                                     pixel_type());
59462       if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
59463       else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
59464       res.assign(N);
59465       _cimg_unserialize_case("bool",bool);
59466       _cimg_unserialize_case("unsigned_char",unsigned char);
59467       _cimg_unserialize_case("uchar",unsigned char);
59468       _cimg_unserialize_case("char",char);
59469       _cimg_unserialize_case("unsigned_short",unsigned short);
59470       _cimg_unserialize_case("ushort",unsigned short);
59471       _cimg_unserialize_case("short",short);
59472       _cimg_unserialize_case("unsigned_int",unsigned int);
59473       _cimg_unserialize_case("uint",unsigned int);
59474       _cimg_unserialize_case("int",int);
59475       _cimg_unserialize_case("unsigned_int64",uint64T);
59476       _cimg_unserialize_case("uint64",uint64T);
59477       _cimg_unserialize_case("int64",int64T);
59478       _cimg_unserialize_case("float",float);
59479       _cimg_unserialize_case("double",double);
59480       if (!loaded)
59481         throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined "
59482                                     "in serialized buffer.",
59483                                     pixel_type(),str_pixeltype._data);
59484       return res;
59485     }
59486 
59487     //@}
59488     //----------------------------------
59489     //
59490     //! \name Others
59491     //@{
59492     //----------------------------------
59493 
59494     //! Crop font along the X-axis.
59495     /**
59496     **/
59497     CImgList<T>& crop_font() {
59498       return get_crop_font().move_to(*this);
59499     }
59500 
59501     //! Crop font along the X-axis \newinstance.
59502     /**
59503     **/
59504     CImgList<T> get_crop_font() const {
59505       CImgList<T> res;
59506       cimglist_for(*this,l) {
59507         const CImg<T>& letter = (*this)[l];
59508         int xmin = letter.width(), xmax = 0;
59509         cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; }
59510         if (xmin>xmax) CImg<T>(letter._width,letter._height,1,letter._spectrum,0).move_to(res);
59511         else letter.get_crop(xmin,0,xmax,letter._height - 1).move_to(res);
59512       }
59513       res[' '].resize(res['f']._width,-100,-100,-100,0);
59514       if (' ' + 256<res.size()) res[' ' + 256].resize(res['f']._width,-100,-100,-100,0);
59515       return res;
59516     }
59517 
59518     //! Return a CImg pre-defined font with desired size.
59519     /**
59520        \param font_height Height of the desired font (exact match for 13,23,53,103).
59521        \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
59522     **/
59523     static const CImgList<ucharT>& font(const unsigned int font_height, const bool is_variable_width=true) {
59524       if (!font_height) return CImgList<ucharT>::const_empty();
59525       cimg::mutex(11);
59526 
59527       // Decompress nearest base font data if needed.
59528       static const char *data_fonts[] = { cimg::data_font12x13, cimg::data_font20x23, cimg::data_font47x53, 0 };
59529       static const unsigned int data_widths[] = { 12,20,47,90 }, data_heights[] = { 13,23,53,103 },
59530                                 data_Ms[] = { 86,79,57,47 };
59531       const unsigned int data_ind = font_height<=13U?0U:font_height<=23U?1U:font_height<=53U?2U:3U;
59532       static CImg<ucharT> base_fonts[4];
59533       CImg<ucharT> &base_font = base_fonts[data_ind];
59534       if (!base_font) {
59535         const unsigned int w = data_widths[data_ind], h = data_heights[data_ind], M = data_Ms[data_ind];
59536         base_font.assign(256*w,h);
59537         const char *data_font = data_fonts[data_ind];
59538         unsigned char *ptrd = base_font;
59539         const unsigned char *const ptrde = base_font.end();
59540 
59541         // Special case needed for 90x103 to avoid MS compiler limit with big strings.
59542         CImg<char> data90x103;
59543         if (!data_font) {
59544           ((CImg<char>(cimg::_data_font90x103[0],
59545                        (unsigned int)std::strlen(cimg::_data_font90x103[0]),1,1,1,true),
59546             CImg<char>(cimg::_data_font90x103[1],
59547                        (unsigned int)std::strlen(cimg::_data_font90x103[1]) + 1,1,1,1,true))>'x').
59548             move_to(data90x103);
59549           data_font = data90x103.data();
59550         }
59551 
59552         // Uncompress font data (decode RLE).
59553         for (const char *ptrs = data_font; *ptrs; ++ptrs) {
59554           const int c = (int)(*ptrs - M - 32), v = c>=0?255:0, n = c>=0?c:-c;
59555           if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
59556           else { std::memset(ptrd,v,ptrde - ptrd); break; }
59557         }
59558       }
59559 
59560       // Find optimal font cache location to return.
59561       static CImgList<ucharT> fonts[16];
59562       static bool is_variable_widths[16] = { 0 };
59563       unsigned int ind = ~0U;
59564       for (int i = 0; i<16; ++i)
59565         if (!fonts[i] || (is_variable_widths[i]==is_variable_width && font_height==fonts[i][0]._height)) {
59566           ind = (unsigned int)i; break; // Found empty slot or cached font.
59567         }
59568       if (ind==~0U) { // No empty slots nor existing font in cache.
59569         fonts->assign();
59570         std::memmove(fonts,fonts + 1,15*sizeof(CImgList<ucharT>));
59571         std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool));
59572         std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font.
59573       }
59574       CImgList<ucharT> &font = fonts[ind];
59575 
59576       // Render requested font.
59577       if (!font) {
59578         const unsigned int padding_x = font_height<33U?1U:font_height<53U?2U:font_height<103U?3U:4U;
59579         is_variable_widths[ind] = is_variable_width;
59580         font = base_font.get_split('x',256);
59581         if (font_height!=font[0]._height)
59582           cimglist_for(font,l)
59583             font[l].resize(std::max(1U,font[l]._width*font_height/font[l]._height),font_height,-100,-100,
59584                            font[0]._height>font_height?2:5);
59585         if (is_variable_width) font.crop_font();
59586         cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,1,1,0,0,0.5);
59587         font.insert(256,0);
59588         cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,3,1);
59589       }
59590       cimg::mutex(11,0);
59591       return font;
59592     }
59593 
59594     //! Compute a 1d Fast Fourier Transform, along specified axis.
59595     /**
59596        \param axis Axis along which the Fourier transform is computed.
59597        \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
59598     **/
59599     CImgList<T>& FFT(const char axis, const bool invert=false) {
59600       if (is_empty()) return *this;
59601       if (_width==1) insert(1);
59602       if (_width>2)
59603         cimg::warn(_cimglist_instance
59604                    "FFT(): Instance has more than 2 images",
59605                    cimglist_instance);
59606 
59607       CImg<T>::FFT(_data[0],_data[1],axis,invert);
59608       return *this;
59609     }
59610 
59611     //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
59612     CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
59613       return CImgList<Tfloat>(*this,false).FFT(axis,invert);
59614     }
59615 
59616     //! Compute a n-d Fast Fourier Transform.
59617     /**
59618       \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
59619     **/
59620     CImgList<T>& FFT(const bool invert=false) {
59621       if (is_empty()) return *this;
59622       if (_width==1) insert(1);
59623       if (_width>2)
59624         cimg::warn(_cimglist_instance
59625                    "FFT(): Instance has more than 2 images",
59626                    cimglist_instance);
59627 
59628       CImg<T>::FFT(_data[0],_data[1],invert);
59629       return *this;
59630     }
59631 
59632     //! Compute a n-d Fast Fourier Transform \newinstance.
59633     CImgList<Tfloat> get_FFT(const bool invert=false) const {
59634       return CImgList<Tfloat>(*this,false).FFT(invert);
59635     }
59636 
59637     //! Reverse primitives orientations of a 3d object.
59638     /**
59639     **/
59640     CImgList<T>& reverse_object3d() {
59641       cimglist_for(*this,l) {
59642         CImg<T>& p = _data[l];
59643         switch (p.size()) {
59644         case 2 : case 3: cimg::swap(p[0],p[1]); break;
59645         case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
59646         case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
59647         case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break;
59648         case 12 : cimg::swap(p[0],p[1],p[2],p[3],p[4],p[6],p[5],p[7],p[8],p[10],p[9],p[11]); break;
59649         }
59650       }
59651       return *this;
59652     }
59653 
59654     //! Reverse primitives orientations of a 3d object \newinstance.
59655     CImgList<T> get_reverse_object3d() const {
59656       return (+*this).reverse_object3d();
59657     }
59658 
59659     //@}
59660   }; // struct CImgList<T> { ...
59661 
59662   /*
59663     #---------------------------------------------
59664     #
59665     # Completion of previously declared functions
59666     #
59667     #----------------------------------------------
59668   */
59669 
59670 namespace cimg {
59671 
59672   // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
59673   // (throw a CImgIOException when macro 'cimg_use_r' is defined).
59674   inline FILE* _stdin(const bool throw_exception) {
59675 #ifndef cimg_use_r
59676     cimg::unused(throw_exception);
59677     return stdin;
59678 #else
59679     if (throw_exception) {
59680       cimg::exception_mode(0);
59681       throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
59682                             "('cimg_use_r' is defined).");
59683     }
59684     return 0;
59685 #endif
59686   }
59687 
59688   inline FILE* _stdout(const bool throw_exception) {
59689 #ifndef cimg_use_r
59690     cimg::unused(throw_exception);
59691     return stdout;
59692 #else
59693     if (throw_exception) {
59694       cimg::exception_mode(0);
59695       throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode "
59696                             "('cimg_use_r' is defined).");
59697     }
59698     return 0;
59699 #endif
59700   }
59701 
59702   inline FILE* _stderr(const bool throw_exception) {
59703 #ifndef cimg_use_r
59704     cimg::unused(throw_exception);
59705     return stderr;
59706 #else
59707     if (throw_exception) {
59708       cimg::exception_mode(0);
59709       throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode "
59710                             "('cimg_use_r' is defined).");
59711     }
59712     return 0;
59713 #endif
59714   }
59715 
59716   // Open a file (with wide character support on Windows).
59717   inline std::FILE *win_fopen(const char *const path, const char *const mode) {
59718 #if cimg_OS==2
59719     // Convert 'path' to a wide-character string.
59720     int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
59721     if (!err) return std_fopen(path,mode);
59722     CImg<wchar_t> wpath(err);
59723     err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err);
59724     if (!err) return std_fopen(path,mode);
59725 
59726     // Convert 'mode' to a wide-character string.
59727     err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0);
59728     if (!err) return std_fopen(path,mode);
59729     CImg<wchar_t> wmode(err);
59730     err = MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err);
59731     if (!err) return std_fopen(path,mode);
59732     return _wfopen(wpath,wmode);
59733 #else
59734     return std_fopen(path,mode);
59735 #endif
59736   }
59737 
59738   //! Get/set path to store temporary files.
59739   /**
59740      \param user_path Specified path, or \c 0 to get the path currently used.
59741      \param reinit_path Force path to be recalculated (may take some time).
59742      \return Path where temporary files can be saved.
59743   **/
59744   inline const char* temporary_path(const char *const user_path, const bool reinit_path) {
59745 #define _cimg_test_temporary_path(p)                                    \
59746     if (!path_found) {                                                  \
59747       cimg_snprintf(s_path,s_path.width(),"%s",p);                      \
59748       cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \
59749       if ((file=std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
59750     }
59751     static CImg<char> s_path;
59752     cimg::mutex(7);
59753     if (reinit_path) s_path.assign();
59754     if (user_path) {
59755       if (!s_path) s_path.assign(1024);
59756       std::strncpy(s_path,user_path,1023);
59757     } else if (!s_path) {
59758       s_path.assign(1024);
59759       bool path_found = false;
59760       CImg<char> tmp(1024), filename_tmp(256);
59761       std::FILE *file = 0;
59762       cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand());
59763       char *tmpPath = std::getenv("TMP");
59764       if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
59765       if (tmpPath) _cimg_test_temporary_path(tmpPath);
59766 #if cimg_OS==2
59767       _cimg_test_temporary_path("C:\\WINNT\\Temp");
59768       _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
59769       _cimg_test_temporary_path("C:\\Temp");
59770       _cimg_test_temporary_path("C:");
59771       _cimg_test_temporary_path("D:\\WINNT\\Temp");
59772       _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
59773       _cimg_test_temporary_path("D:\\Temp");
59774       _cimg_test_temporary_path("D:");
59775 #else
59776       _cimg_test_temporary_path("/tmp");
59777       _cimg_test_temporary_path("/var/tmp");
59778 #endif
59779       if (!path_found) {
59780         *s_path = 0;
59781         std::strncpy(tmp,filename_tmp,tmp._width - 1);
59782         if ((file=std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
59783       }
59784       if (!path_found) {
59785         cimg::mutex(7,0);
59786         throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
59787       }
59788     }
59789     cimg::mutex(7,0);
59790     return s_path;
59791   }
59792 
59793   //! Get/set path to the <i>Program Files/</i> directory (Windows only).
59794   /**
59795      \param user_path Specified path, or \c 0 to get the path currently used.
59796      \param reinit_path Force path to be recalculated (may take some time).
59797      \return Path containing the program files.
59798   **/
59799 #if cimg_OS==2
59800   inline const char* programfiles_path(const char *const user_path, const bool reinit_path) {
59801     static CImg<char> s_path;
59802     cimg::mutex(7);
59803     if (reinit_path) s_path.assign();
59804     if (user_path) {
59805       if (!s_path) s_path.assign(1024);
59806       std::strncpy(s_path,user_path,1023);
59807     } else if (!s_path) {
59808       s_path.assign(MAX_PATH);
59809       *s_path = 0;
59810       // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
59811 #if !defined(__INTEL_COMPILER)
59812       if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) {
59813         const char *const pfPath = std::getenv("PROGRAMFILES");
59814         if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1);
59815         else std::strcpy(s_path,"C:\\PROGRA~1");
59816       }
59817 #else
59818       std::strcpy(s_path,"C:\\PROGRA~1");
59819 #endif
59820     }
59821     cimg::mutex(7,0);
59822     return s_path;
59823   }
59824 #endif
59825 
59826   //! Get/set path to the ImageMagick's \c convert binary.
59827   /**
59828      \param user_path Specified path, or \c 0 to get the path currently used.
59829      \param reinit_path Force path to be recalculated (may take some time).
59830      \return Path containing the \c convert binary.
59831   **/
59832   inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) {
59833     static CImg<char> s_path;
59834     cimg::mutex(7);
59835     if (reinit_path) s_path.assign();
59836     if (user_path) {
59837       if (!s_path) s_path.assign(1024);
59838       std::strncpy(s_path,user_path,1023);
59839     } else if (!s_path) {
59840       s_path.assign(1024);
59841       bool path_found = false;
59842       std::FILE *file = 0;
59843 #if cimg_OS==2
59844       const char *const pf_path = programfiles_path();
59845       for (int l = 0; l<2 && !path_found; ++l) {
59846         const char *const s_exe = l?"convert":"magick";
59847         cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe);
59848         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59849         for (int k = 32; k>=10 && !path_found; --k) {
59850           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe);
59851           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59852         }
59853         for (int k = 9; k>=0 && !path_found; --k) {
59854           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe);
59855           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59856         }
59857         for (int k = 32; k>=0 && !path_found; --k) {
59858           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe);
59859           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59860         }
59861         for (int k = 32; k>=10 && !path_found; --k) {
59862           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
59863           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59864         }
59865         for (int k = 9; k>=0 && !path_found; --k) {
59866           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
59867           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59868         }
59869         for (int k = 32; k>=0 && !path_found; --k) {
59870           cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
59871           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59872         }
59873         for (int k = 32; k>=10 && !path_found; --k) {
59874           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
59875           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59876         }
59877         for (int k = 9; k>=0 && !path_found; --k) {
59878           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
59879           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59880         }
59881         for (int k = 32; k>=0 && !path_found; --k) {
59882           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
59883           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59884         }
59885         for (int k = 32; k>=10 && !path_found; --k) {
59886           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59887           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59888         }
59889         for (int k = 9; k>=0 && !path_found; --k) {
59890           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59891           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59892         }
59893         for (int k = 32; k>=0 && !path_found; --k) {
59894           cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59895           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59896         }
59897         for (int k = 32; k>=10 && !path_found; --k) {
59898           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
59899           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59900         }
59901         for (int k = 9; k>=0 && !path_found; --k) {
59902           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
59903           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59904         }
59905         for (int k = 32; k>=0 && !path_found; --k) {
59906           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
59907           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59908         }
59909         for (int k = 32; k>=10 && !path_found; --k) {
59910           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59911           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59912         }
59913         for (int k = 9; k>=0 && !path_found; --k) {
59914           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59915           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59916         }
59917         for (int k = 32; k>=0 && !path_found; --k) {
59918           cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
59919           if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59920         }
59921         if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe);
59922       }
59923 #else
59924       std::strcpy(s_path,"./magick");
59925       if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59926       if (!path_found) {
59927         std::strcpy(s_path,"./convert");
59928         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59929       }
59930       if (!path_found) std::strcpy(s_path,"convert");
59931 #endif
59932       winformat_string(s_path);
59933     }
59934     cimg::mutex(7,0);
59935     return s_path;
59936   }
59937 
59938   //! Get/set path to the GraphicsMagick's \c gm binary.
59939   /**
59940      \param user_path Specified path, or \c 0 to get the path currently used.
59941      \param reinit_path Force path to be recalculated (may take some time).
59942      \return Path containing the \c gm binary.
59943   **/
59944   inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) {
59945     static CImg<char> s_path;
59946     cimg::mutex(7);
59947     if (reinit_path) s_path.assign();
59948     if (user_path) {
59949       if (!s_path) s_path.assign(1024);
59950       std::strncpy(s_path,user_path,1023);
59951     } else if (!s_path) {
59952       s_path.assign(1024);
59953       bool path_found = false;
59954       std::FILE *file = 0;
59955 #if cimg_OS==2
59956       const char *const pf_path = programfiles_path();
59957       if (!path_found) {
59958         std::strcpy(s_path,".\\gm.exe");
59959         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59960       }
59961       for (int k = 32; k>=10 && !path_found; --k) {
59962         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
59963         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59964       }
59965       for (int k = 9; k>=0 && !path_found; --k) {
59966         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
59967         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59968       }
59969       for (int k = 32; k>=0 && !path_found; --k) {
59970         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
59971         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59972       }
59973       for (int k = 32; k>=10 && !path_found; --k) {
59974         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
59975         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59976       }
59977       for (int k = 9; k>=0 && !path_found; --k) {
59978         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
59979         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59980       }
59981       for (int k = 32; k>=0 && !path_found; --k) {
59982         cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
59983         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59984       }
59985       for (int k = 32; k>=10 && !path_found; --k) {
59986         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
59987         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59988       }
59989       for (int k = 9; k>=0 && !path_found; --k) {
59990         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
59991         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59992       }
59993       for (int k = 32; k>=0 && !path_found; --k) {
59994         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k);
59995         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
59996       }
59997       for (int k = 32; k>=10 && !path_found; --k) {
59998         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
59999         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60000       }
60001       for (int k = 9; k>=0 && !path_found; --k) {
60002         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
60003         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60004       }
60005       for (int k = 32; k>=0 && !path_found; --k) {
60006         cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
60007         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60008       }
60009       for (int k = 32; k>=10 && !path_found; --k) {
60010         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
60011         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60012       }
60013       for (int k = 9; k>=0 && !path_found; --k) {
60014         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
60015         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60016       }
60017       for (int k = 32; k>=0 && !path_found; --k) {
60018         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k);
60019         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60020       }
60021       for (int k = 32; k>=10 && !path_found; --k) {
60022         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
60023         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60024       }
60025       for (int k = 9; k>=0 && !path_found; --k) {
60026         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
60027         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60028       }
60029       for (int k = 32; k>=0 && !path_found; --k) {
60030         cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
60031         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60032       }
60033       if (!path_found) std::strcpy(s_path,"gm.exe");
60034 #else
60035       if (!path_found) {
60036         std::strcpy(s_path,"./gm");
60037         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60038       }
60039       if (!path_found) std::strcpy(s_path,"gm");
60040 #endif
60041       winformat_string(s_path);
60042     }
60043     cimg::mutex(7,0);
60044     return s_path;
60045   }
60046 
60047   //! Get/set path to the XMedcon's \c medcon binary.
60048   /**
60049      \param user_path Specified path, or \c 0 to get the path currently used.
60050      \param reinit_path Force path to be recalculated (may take some time).
60051      \return Path containing the \c medcon binary.
60052   **/
60053   inline const char* medcon_path(const char *const user_path, const bool reinit_path) {
60054     static CImg<char> s_path;
60055     cimg::mutex(7);
60056     if (reinit_path) s_path.assign();
60057     if (user_path) {
60058       if (!s_path) s_path.assign(1024);
60059       std::strncpy(s_path,user_path,1023);
60060     } else if (!s_path) {
60061       s_path.assign(1024);
60062       bool path_found = false;
60063       std::FILE *file = 0;
60064 #if cimg_OS==2
60065       const char *const pf_path = programfiles_path();
60066       if (!path_found) {
60067         std::strcpy(s_path,".\\medcon.exe");
60068         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60069       }
60070       if (!path_found) {
60071         cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path);
60072         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60073       }
60074       if (!path_found) {
60075         cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path);
60076         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60077       }
60078       if (!path_found) {
60079         std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe");
60080         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60081       }
60082       if (!path_found) std::strcpy(s_path,"medcon.exe");
60083 #else
60084       if (!path_found) {
60085         std::strcpy(s_path,"./medcon");
60086         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60087       }
60088       if (!path_found) std::strcpy(s_path,"medcon");
60089 #endif
60090       winformat_string(s_path);
60091     }
60092     cimg::mutex(7,0);
60093     return s_path;
60094   }
60095 
60096   //! Get/set path to the FFMPEG's \c ffmpeg binary.
60097   /**
60098      \param user_path Specified path, or \c 0 to get the path currently used.
60099      \param reinit_path Force path to be recalculated (may take some time).
60100      \return Path containing the \c ffmpeg binary.
60101   **/
60102   inline const char *ffmpeg_path(const char *const user_path, const bool reinit_path) {
60103     static CImg<char> s_path;
60104     cimg::mutex(7);
60105     if (reinit_path) s_path.assign();
60106     if (user_path) {
60107       if (!s_path) s_path.assign(1024);
60108       std::strncpy(s_path,user_path,1023);
60109     } else if (!s_path) {
60110       s_path.assign(1024);
60111       bool path_found = false;
60112       std::FILE *file = 0;
60113 #if cimg_OS==2
60114       if (!path_found) {
60115         std::strcpy(s_path,".\\ffmpeg.exe");
60116         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60117       }
60118       if (!path_found) std::strcpy(s_path,"ffmpeg.exe");
60119 #else
60120       if (!path_found) {
60121         std::strcpy(s_path,"./ffmpeg");
60122         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60123       }
60124       if (!path_found) std::strcpy(s_path,"ffmpeg");
60125 #endif
60126       winformat_string(s_path);
60127     }
60128     cimg::mutex(7,0);
60129     return s_path;
60130   }
60131 
60132   //! Get/set path to the \c gzip binary.
60133   /**
60134      \param user_path Specified path, or \c 0 to get the path currently used.
60135      \param reinit_path Force path to be recalculated (may take some time).
60136      \return Path containing the \c gzip binary.
60137   **/
60138   inline const char *gzip_path(const char *const user_path, const bool reinit_path) {
60139     static CImg<char> s_path;
60140     cimg::mutex(7);
60141     if (reinit_path) s_path.assign();
60142     if (user_path) {
60143       if (!s_path) s_path.assign(1024);
60144       std::strncpy(s_path,user_path,1023);
60145     } else if (!s_path) {
60146       s_path.assign(1024);
60147       bool path_found = false;
60148       std::FILE *file = 0;
60149 #if cimg_OS==2
60150       if (!path_found) {
60151         std::strcpy(s_path,".\\gzip.exe");
60152         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60153       }
60154       if (!path_found) std::strcpy(s_path,"gzip.exe");
60155 #else
60156       if (!path_found) {
60157         std::strcpy(s_path,"./gzip");
60158         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60159       }
60160       if (!path_found) std::strcpy(s_path,"gzip");
60161 #endif
60162       winformat_string(s_path);
60163     }
60164     cimg::mutex(7,0);
60165     return s_path;
60166   }
60167 
60168   //! Get/set path to the \c gunzip binary.
60169   /**
60170      \param user_path Specified path, or \c 0 to get the path currently used.
60171      \param reinit_path Force path to be recalculated (may take some time).
60172      \return Path containing the \c gunzip binary.
60173   **/
60174   inline const char *gunzip_path(const char *const user_path, const bool reinit_path) {
60175     static CImg<char> s_path;
60176     cimg::mutex(7);
60177     if (reinit_path) s_path.assign();
60178     if (user_path) {
60179       if (!s_path) s_path.assign(1024);
60180       std::strncpy(s_path,user_path,1023);
60181     } else if (!s_path) {
60182       s_path.assign(1024);
60183       bool path_found = false;
60184       std::FILE *file = 0;
60185 #if cimg_OS==2
60186       if (!path_found) {
60187         std::strcpy(s_path,".\\gunzip.exe");
60188         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60189       }
60190       if (!path_found) std::strcpy(s_path,"gunzip.exe");
60191 #else
60192       if (!path_found) {
60193         std::strcpy(s_path,"./gunzip");
60194         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60195       }
60196       if (!path_found) std::strcpy(s_path,"gunzip");
60197 #endif
60198       winformat_string(s_path);
60199     }
60200     cimg::mutex(7,0);
60201     return s_path;
60202   }
60203 
60204   //! Get/set path to the \c dcraw binary.
60205   /**
60206      \param user_path Specified path, or \c 0 to get the path currently used.
60207      \param reinit_path Force path to be recalculated (may take some time).
60208      \return Path containing the \c dcraw binary.
60209   **/
60210   inline const char *dcraw_path(const char *const user_path, const bool reinit_path) {
60211     static CImg<char> s_path;
60212     cimg::mutex(7);
60213     if (reinit_path) s_path.assign();
60214     if (user_path) {
60215       if (!s_path) s_path.assign(1024);
60216       std::strncpy(s_path,user_path,1023);
60217     } else if (!s_path) {
60218       s_path.assign(1024);
60219       bool path_found = false;
60220       std::FILE *file = 0;
60221 #if cimg_OS==2
60222       if (!path_found) {
60223         std::strcpy(s_path,".\\dcraw.exe");
60224         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60225       }
60226       if (!path_found) std::strcpy(s_path,"dcraw.exe");
60227 #else
60228       if (!path_found) {
60229         std::strcpy(s_path,"./dcraw");
60230         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60231       }
60232       if (!path_found) std::strcpy(s_path,"dcraw");
60233 #endif
60234       winformat_string(s_path);
60235     }
60236     cimg::mutex(7,0);
60237     return s_path;
60238   }
60239 
60240   //! Get/set path to the \c wget binary.
60241   /**
60242      \param user_path Specified path, or \c 0 to get the path currently used.
60243      \param reinit_path Force path to be recalculated (may take some time).
60244      \return Path containing the \c wget binary.
60245   **/
60246   inline const char *wget_path(const char *const user_path, const bool reinit_path) {
60247     static CImg<char> s_path;
60248     cimg::mutex(7);
60249     if (reinit_path) s_path.assign();
60250     if (user_path) {
60251       if (!s_path) s_path.assign(1024);
60252       std::strncpy(s_path,user_path,1023);
60253     } else if (!s_path) {
60254       s_path.assign(1024);
60255       bool path_found = false;
60256       std::FILE *file = 0;
60257 #if cimg_OS==2
60258       if (!path_found) {
60259         std::strcpy(s_path,".\\wget.exe");
60260         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60261       }
60262       if (!path_found) std::strcpy(s_path,"wget.exe");
60263 #else
60264       if (!path_found) {
60265         std::strcpy(s_path,"./wget");
60266         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60267       }
60268       if (!path_found) std::strcpy(s_path,"wget");
60269 #endif
60270       winformat_string(s_path);
60271     }
60272     cimg::mutex(7,0);
60273     return s_path;
60274   }
60275 
60276   //! Get/set path to the \c curl binary.
60277   /**
60278      \param user_path Specified path, or \c 0 to get the path currently used.
60279      \param reinit_path Force path to be recalculated (may take some time).
60280      \return Path containing the \c curl binary.
60281   **/
60282   inline const char *curl_path(const char *const user_path, const bool reinit_path) {
60283     static CImg<char> s_path;
60284     cimg::mutex(7);
60285     if (reinit_path) s_path.assign();
60286     if (user_path) {
60287       if (!s_path) s_path.assign(1024);
60288       std::strncpy(s_path,user_path,1023);
60289     } else if (!s_path) {
60290       s_path.assign(1024);
60291       bool path_found = false;
60292       std::FILE *file = 0;
60293 #if cimg_OS==2
60294       if (!path_found) {
60295         std::strcpy(s_path,".\\curl.exe");
60296         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60297       }
60298       if (!path_found) std::strcpy(s_path,"curl.exe");
60299 #else
60300       if (!path_found) {
60301         std::strcpy(s_path,"./curl");
60302         if ((file=std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
60303       }
60304       if (!path_found) std::strcpy(s_path,"curl");
60305 #endif
60306       winformat_string(s_path);
60307     }
60308     cimg::mutex(7,0);
60309     return s_path;
60310   }
60311 
60312   // [internal] Sorting function, used by cimg::files().
60313   inline int _sort_files(const void* a, const void* b) {
60314     const CImg<char> &sa = *(CImg<char>*)a, &sb = *(CImg<char>*)b;
60315     return std::strcmp(sa._data,sb._data);
60316   }
60317 
60318   //! Return list of files/directories in specified directory.
60319   /**
60320      \param path Path to the directory. Set to 0 for current directory.
60321      \param is_pattern Tell if specified path has a matching pattern in it.
60322      \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }.
60323      \param include_path Tell if \c path must be included in resulting filenames.
60324      \return A list of filenames.
60325   **/
60326   inline CImgList<char> files(const char *const path, const bool is_pattern=false,
60327                               const unsigned int mode=2, const bool include_path=false) {
60328     if (!path || !*path) return files("*",true,mode,include_path);
60329     CImgList<char> res;
60330 
60331     // If path is a valid folder name, ignore argument 'is_pattern'.
60332     const bool _is_pattern = is_pattern && !cimg::is_directory(path);
60333     bool is_root = false, is_current = false;
60334     cimg::unused(is_root,is_current);
60335 
60336     // Clean format of input path.
60337     CImg<char> pattern, _path = CImg<char>::string(path);
60338 #if cimg_OS==2
60339     for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/';
60340 #endif
60341     char *pd = _path;
60342     for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; }
60343     *pd = 0;
60344     unsigned int lp = (unsigned int)std::strlen(_path);
60345     if (!_is_pattern && lp && _path[lp - 1]=='/') {
60346       _path[lp - 1] = 0; --lp;
60347 #if cimg_OS!=2
60348       is_root = !*_path;
60349 #endif
60350     }
60351 
60352     // Separate folder path and matching pattern.
60353     if (_is_pattern) {
60354       const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data());
60355       CImg<char>::string(_path).move_to(pattern);
60356       if (bpos) {
60357         _path[bpos - 1] = 0; // End 'path' at last slash.
60358 #if cimg_OS!=2
60359         is_root = !*_path;
60360 #endif
60361       } else { // No path to folder specified, assuming current folder.
60362         is_current = true; *_path = 0;
60363       }
60364       lp = (unsigned int)std::strlen(_path);
60365     }
60366 
60367     // Windows version.
60368 #if cimg_OS==2
60369     if (!_is_pattern) {
60370       pattern.assign(lp + 3);
60371       std::memcpy(pattern,_path,lp);
60372       pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0;
60373     }
60374     WIN32_FIND_DATAA file_data;
60375     const HANDLE dir = FindFirstFileA(pattern.data(),&file_data);
60376     if (dir==INVALID_HANDLE_VALUE) return CImgList<char>::const_empty();
60377     do {
60378       const char *const filename = file_data.cFileName;
60379       if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
60380         const unsigned int lf = (unsigned int)std::strlen(filename);
60381         const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0;
60382         if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) {
60383           if (include_path) {
60384             CImg<char> full_filename((lp?lp+1:0) + lf + 1);
60385             if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; }
60386             std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1);
60387             full_filename.move_to(res);
60388           } else CImg<char>(filename,lf + 1).move_to(res);
60389         }
60390       }
60391     } while (FindNextFileA(dir,&file_data));
60392     FindClose(dir);
60393 
60394     // Unix version (posix).
60395 #elif cimg_OS == 1
60396     DIR *const dir = opendir(is_root?"/":is_current?".":_path.data());
60397     if (!dir) return CImgList<char>::const_empty();
60398     struct dirent *ent;
60399     while ((ent=readdir(dir))!=0) {
60400       const char *const filename = ent->d_name;
60401       if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
60402         const unsigned int lf = (unsigned int)std::strlen(filename);
60403         CImg<char> full_filename(lp + lf + 2);
60404 
60405         if (!is_current) {
60406           full_filename.assign(lp + lf + 2);
60407           if (lp) std::memcpy(full_filename,_path,lp);
60408           full_filename[lp] = '/';
60409           std::memcpy(full_filename._data + lp + 1,filename,lf + 1);
60410         } else full_filename.assign(filename,lf + 1);
60411 
60412         struct stat st;
60413         if (stat(full_filename,&st)==-1) continue;
60414         const bool is_directory = (st.st_mode & S_IFDIR)!=0;
60415         if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) {
60416           if (include_path) {
60417             if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
60418               full_filename.move_to(res);
60419           } else {
60420             if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
60421               CImg<char>(filename,lf + 1).move_to(res);
60422           }
60423         }
60424       }
60425     }
60426     closedir(dir);
60427 #endif
60428 
60429     // Sort resulting list by lexicographic order.
60430     if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg<char>),_sort_files);
60431 
60432     return res;
60433   }
60434 
60435   //! Try to guess format from an image file.
60436   /**
60437      \param file Input file (can be \c 0 if \c filename is set).
60438      \param filename Filename, as a C-string (can be \c 0 if \c file is set).
60439      \return C-string containing the guessed file format, or \c 0 if nothing has been guessed.
60440   **/
60441   inline const char *ftype(std::FILE *const file, const char *const filename) {
60442     if (!file && !filename)
60443       throw CImgArgumentException("cimg::ftype(): Specified filename is (null).");
60444     static const char
60445       *const _pnm = "pnm",
60446       *const _pfm = "pfm",
60447       *const _bmp = "bmp",
60448       *const _gif = "gif",
60449       *const _jpg = "jpg",
60450       *const _off = "off",
60451       *const _pan = "pan",
60452       *const _png = "png",
60453       *const _tif = "tif",
60454       *const _inr = "inr",
60455       *const _dcm = "dcm";
60456     const char *f_type = 0;
60457     CImg<char> header;
60458     const unsigned int omode = cimg::exception_mode();
60459     cimg::exception_mode(0);
60460     try {
60461       header._load_raw(file,filename,512,1,1,1,false,false,0);
60462       const unsigned char *const uheader = (unsigned char*)header._data;
60463       if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF.
60464       else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // INRIMAGE.
60465       else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // PANDORE.
60466       else if (!std::strncmp(header.data() + 128,"DICM",4)) f_type = _dcm; // DICOM.
60467       else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg;  // JPEG.
60468       else if (header[0]=='B' && header[1]=='M') f_type = _bmp;  // BMP.
60469       else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // GIF.
60470                (header[4]=='7' || header[4]=='9')) f_type = _gif;
60471       else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&  // PNG.
60472                uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png;
60473       else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // TIFF.
60474       else { // PNM or PFM.
60475         CImgList<char> _header = header.get_split(CImg<char>::vector('\n'),0,false);
60476         cimglist_for(_header,l) {
60477           if (_header(l,0)=='#') continue;
60478           if (_header[l]._height==2 && _header(l,0)=='P') {
60479             const char c = _header(l,1);
60480             if (c=='f' || c=='F') { f_type = _pfm; break; }
60481             if (c>='1' && c<='9') { f_type = _pnm; break; }
60482           }
60483           f_type = 0; break;
60484         }
60485       }
60486     } catch (CImgIOException&) { }
60487     cimg::exception_mode(omode);
60488     return f_type;
60489   }
60490 
60491   //! Load file from network as a local temporary file.
60492   /**
60493      \param url URL of the filename, as a C-string.
60494      \param[out] filename_local C-string containing the path to a local copy of \c filename.
60495      \param timeout Maximum time (in seconds) authorized for downloading the file from the URL.
60496      \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure.
60497      \param referer Referer used, as a C-string.
60498      \return Value of \c filename_local.
60499      \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download.
60500   **/
60501   inline char *load_network(const char *const url, char *const filename_local,
60502                             const unsigned int timeout, const bool try_fallback,
60503                             const char *const referer) {
60504     if (!url)
60505       throw CImgArgumentException("cimg::load_network(): Specified URL is (null).");
60506     if (!filename_local)
60507       throw CImgArgumentException("cimg::load_network(): Specified destination string is (null).");
60508 
60509     const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext;
60510     CImg<char> ext = CImg<char>::string(_ext);
60511     std::FILE *file = 0;
60512     *filename_local = 0;
60513     if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0;
60514     else cimg::strwindows_reserved(ext);
60515     do {
60516       cimg_snprintf(filename_local,256,"%s%c%s%s",
60517                     cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data);
60518       if ((file=std_fopen(filename_local,"rb"))!=0) cimg::fclose(file);
60519     } while (file);
60520 
60521 #ifdef cimg_use_curl
60522     const unsigned int omode = cimg::exception_mode();
60523     cimg::exception_mode(0);
60524     try {
60525       CURL *curl = 0;
60526       CURLcode res;
60527       curl = curl_easy_init();
60528       if (curl) {
60529         file = cimg::fopen(filename_local,"wb");
60530         curl_easy_setopt(curl,CURLOPT_URL,url);
60531         curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0);
60532         curl_easy_setopt(curl,CURLOPT_WRITEDATA,file);
60533         curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
60534         curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L);
60535         curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
60536         if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout);
60537         if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
60538         if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer);
60539         res = curl_easy_perform(curl);
60540         curl_easy_cleanup(curl);
60541         cimg::fseek(file,0,SEEK_END); // Check if file size is 0.
60542         const cimg_ulong siz = cimg::ftell(file);
60543         cimg::fclose(file);
60544         if (siz>0 && res==CURLE_OK) {
60545           cimg::exception_mode(omode);
60546           return filename_local;
60547         } else std::remove(filename_local);
60548       }
60549     } catch (...) { }
60550     cimg::exception_mode(omode);
60551     if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url);
60552 #endif
60553 
60554     CImg<char> command((unsigned int)std::strlen(url) + 64);
60555     cimg::unused(try_fallback);
60556 
60557     // Try with 'curl' first.
60558     if (timeout) {
60559       if (referer)
60560         cimg_snprintf(command,command._width,"%s -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"",
60561                       cimg::curl_path(),referer,timeout,filename_local,url);
60562       else
60563         cimg_snprintf(command,command._width,"%s -m %u -f --silent --compressed -o \"%s\" \"%s\"",
60564                       cimg::curl_path(),timeout,filename_local,url);
60565     } else {
60566       if (referer)
60567         cimg_snprintf(command,command._width,"%s -e %s -f --silent --compressed -o \"%s\" \"%s\"",
60568                       cimg::curl_path(),referer,filename_local,url);
60569       else
60570         cimg_snprintf(command,command._width,"%s -f --silent --compressed -o \"%s\" \"%s\"",
60571                       cimg::curl_path(),filename_local,url);
60572     }
60573     cimg::system(command);
60574 
60575     if (!(file = std_fopen(filename_local,"rb"))) {
60576 
60577       // Try with 'wget' otherwise.
60578       if (timeout) {
60579         if (referer)
60580           cimg_snprintf(command,command._width,"%s --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
60581                         cimg::wget_path(),referer,timeout,filename_local,url);
60582         else
60583           cimg_snprintf(command,command._width,"%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
60584                         cimg::wget_path(),timeout,filename_local,url);
60585       } else {
60586         if (referer)
60587           cimg_snprintf(command,command._width,"%s --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
60588                         cimg::wget_path(),referer,filename_local,url);
60589         else
60590           cimg_snprintf(command,command._width,"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",
60591                         cimg::wget_path(),filename_local,url);
60592       }
60593       cimg::system(command);
60594 
60595       if (!(file = std_fopen(filename_local,"rb")))
60596         throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands "
60597                               "'wget' or 'curl'.",url);
60598       cimg::fclose(file);
60599 
60600       // Try gunzip it.
60601       cimg_snprintf(command,command._width,"%s.gz",filename_local);
60602       std::rename(filename_local,command);
60603       cimg_snprintf(command,command._width,"%s --quiet \"%s.gz\"",
60604                     gunzip_path(),filename_local);
60605       cimg::system(command);
60606       file = std_fopen(filename_local,"rb");
60607       if (!file) {
60608         cimg_snprintf(command,command._width,"%s.gz",filename_local);
60609         std::rename(command,filename_local);
60610         file = std_fopen(filename_local,"rb");
60611       }
60612     }
60613     cimg::fseek(file,0,SEEK_END); // Check if file size is 0.
60614     if (std::ftell(file)<=0)
60615       throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands "
60616                             "'wget' or 'curl'.",url);
60617     cimg::fclose(file);
60618     return filename_local;
60619   }
60620 
60621   // Implement a tic/toc mechanism to display elapsed time of algorithms.
60622   inline cimg_ulong tictoc(const bool is_tic) {
60623     cimg::mutex(2);
60624     static CImg<cimg_ulong> times(64);
60625     static unsigned int pos = 0;
60626     const cimg_ulong t1 = cimg::time();
60627     if (is_tic) {
60628       // Tic
60629       times[pos++] = t1;
60630       if (pos>=times._width)
60631         throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
60632       cimg::mutex(2,0);
60633       return t1;
60634     }
60635 
60636     // Toc
60637     if (!pos)
60638       throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
60639     const cimg_ulong
60640       t0 = times[--pos],
60641       dt = t1>=t0?(t1 - t0):cimg::type<cimg_ulong>::max();
60642     const unsigned int
60643       edays = (unsigned int)(dt/86400000.0),
60644       ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0),
60645       emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0),
60646       esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0),
60647       ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0);
60648     if (!edays && !ehours && !emin && !esec)
60649       std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
60650                    cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
60651     else {
60652       if (!edays && !ehours && !emin)
60653         std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
60654                      cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
60655       else {
60656         if (!edays && !ehours)
60657           std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
60658                        cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal);
60659         else{
60660           if (!edays)
60661             std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
60662                          cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
60663           else{
60664             std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
60665                          cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
60666           }
60667         }
60668       }
60669     }
60670     cimg::mutex(2,0);
60671     return dt;
60672   }
60673 
60674   // Return a temporary string describing the size of a memory buffer.
60675   inline const char *strbuffersize(const cimg_ulong size) {
60676     static CImg<char> res(256);
60677     cimg::mutex(5);
60678     if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":"");
60679     else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); }
60680     else if (size<1024*1024*1024LU) {
60681       const float nsize = size/(1024*1024.0f); cimg_snprintf(res,res._width,"%.1f Mio",nsize);
60682     } else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); }
60683     cimg::mutex(5,0);
60684     return res;
60685   }
60686 
60687   //! Display a simple dialog box, and wait for the user's response.
60688   /**
60689      \param title Title of the dialog window.
60690      \param msg Main message displayed inside the dialog window.
60691      \param button1_label Label of the 1st button.
60692      \param button2_label Label of the 2nd button (\c 0 to hide button).
60693      \param button3_label Label of the 3rd button (\c 0 to hide button).
60694      \param button4_label Label of the 4th button (\c 0 to hide button).
60695      \param button5_label Label of the 5th button (\c 0 to hide button).
60696      \param button6_label Label of the 6th button (\c 0 to hide button).
60697      \param logo Image logo displayed at the left of the main message.
60698      \param is_centered Tells if the dialog window must be centered on the screen.
60699      \return Indice of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user.
60700      \note
60701      - Up to 6 buttons can be defined in the dialog window.
60702      - The function returns when a user clicked one of the button or closed the dialog window.
60703      - If a button text is set to 0, the corresponding button (and the followings) will not appear in the dialog box.
60704      At least one button must be specified.
60705   **/
60706   template<typename t>
60707   inline int dialog(const char *const title, const char *const msg,
60708                     const char *const button1_label, const char *const button2_label,
60709                     const char *const button3_label, const char *const button4_label,
60710                     const char *const button5_label, const char *const button6_label,
60711                     const CImg<t>& logo, const bool is_centered=false) {
60712 #if cimg_display==0
60713     cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
60714                  logo._data,is_centered);
60715     throw CImgIOException("cimg::dialog(): No display available.");
60716 #else
60717     static const unsigned char
60718       black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
60719 
60720     // Create buttons and canvas graphics
60721     CImgList<unsigned char> buttons, cbuttons, sbuttons;
60722     if (button1_label) { CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
60723       if (button2_label) { CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
60724         if (button3_label) { CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
60725           if (button4_label) { CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
60726             if (button5_label) { CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
60727               if (button6_label) { CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
60728               }}}}}}
60729     if (!buttons._width)
60730       throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
60731     cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
60732 
60733     unsigned int bw = 0, bh = 0;
60734     cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); }
60735     bw+=8; bh+=8;
60736     if (bw<64) bw = 64;
60737     if (bw>128) bw = 128;
60738     if (bh<24) bh = 24;
60739     if (bh>48) bh = 48;
60740 
60741     CImg<unsigned char> button(bw,bh,1,3);
60742     button.draw_rectangle(0,0,bw - 1,bh - 1,gray);
60743     button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white);
60744     button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black);
60745     button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2);
60746     CImg<unsigned char> sbutton(bw,bh,1,3);
60747     sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray);
60748     sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black);
60749     sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black);
60750     sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white);
60751     sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black);
60752     sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2);
60753     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);
60754     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);
60755     CImg<unsigned char> cbutton(bw,bh,1,3);
60756     cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2).
60757       draw_rectangle(2,2,bw - 3,bh - 3,gray);
60758     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);
60759     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);
60760 
60761     cimglist_for(buttons,ll) {
60762       CImg<unsigned char>(cbutton).
60763         draw_image(1 + (bw  -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]).
60764         move_to(cbuttons);
60765       CImg<unsigned char>(sbutton).
60766         draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
60767         move_to(sbuttons);
60768       CImg<unsigned char>(button).
60769         draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
60770         move_to(buttons[ll]);
60771     }
60772 
60773     CImg<unsigned char> canvas;
60774     if (msg)
60775       ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas);
60776 
60777     const unsigned int
60778       bwall = (buttons._width - 1)*(12 + bw) + bw,
60779       w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall),
60780       h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh),
60781       lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)),
60782       ly = (h - 12 - bh - logo._height)/2,
60783       tx = lx + logo._width + 12,
60784       ty = (h - 12 - bh - canvas._height)/2,
60785       bx = (w - bwall)/2,
60786       by = h - 12 - bh;
60787 
60788     if (canvas._data)
60789       canvas = CImg<unsigned char>(w,h,1,3).
60790         draw_rectangle(0,0,w - 1,h - 1,gray).
60791         draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
60792         draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black).
60793         draw_image(tx,ty,canvas);
60794     else
60795       canvas = CImg<unsigned char>(w,h,1,3).
60796         draw_rectangle(0,0,w - 1,h - 1,gray).
60797         draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
60798         draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black);
60799     if (logo._data) canvas.draw_image(lx,ly,logo);
60800 
60801     unsigned int xbuttons[6] = { 0 };
60802     cimglist_for(buttons,lll) { xbuttons[lll] = bx + (bw + 12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); }
60803 
60804     // Open window and enter events loop
60805     CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
60806     if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
60807                                (CImgDisplay::screen_height() - disp.height())/2);
60808     bool stop_flag = false, refresh = false;
60809     int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
60810     while (!disp.is_closed() && !stop_flag) {
60811       if (refresh) {
60812         if (clicked>=0)
60813           CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
60814         else {
60815           if (selected>=0)
60816             CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
60817           else canvas.display(disp);
60818         }
60819         refresh = false;
60820       }
60821       disp.wait(15);
60822       if (disp.is_resized()) disp.resize(disp,false);
60823 
60824       if (disp.button()&1)  {
60825         oclicked = clicked;
60826         clicked = -1;
60827         cimglist_for(buttons,l)
60828           if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) &&
60829               disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) {
60830             clicked = selected = l;
60831             refresh = true;
60832           }
60833         if (clicked!=oclicked) refresh = true;
60834       } else if (clicked>=0) stop_flag = true;
60835 
60836       if (disp.key()) {
60837         oselected = selected;
60838         switch (disp.key()) {
60839         case cimg::keyESC : selected = -1; stop_flag = true; break;
60840         case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break;
60841         case cimg::keyTAB :
60842         case cimg::keyARROWRIGHT :
60843         case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break;
60844         case cimg::keyARROWLEFT :
60845         case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break;
60846         }
60847         disp.set_key();
60848         if (selected!=oselected) refresh = true;
60849       }
60850     }
60851     if (!disp) selected = -1;
60852     return selected;
60853 #endif
60854   }
60855 
60856   //! Display a simple dialog box, and wait for the user's response \specialization.
60857   inline int dialog(const char *const title, const char *const msg,
60858                     const char *const button1_label, const char *const button2_label, const char *const button3_label,
60859                     const char *const button4_label, const char *const button5_label, const char *const button6_label,
60860                     const bool is_centered) {
60861     return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
60862                   CImg<unsigned char>::_logo40x38(),is_centered);
60863   }
60864 
60865   //! Evaluate math expression.
60866   /**
60867      \param expression C-string describing the formula to evaluate.
60868      \param x Value of the pre-defined variable \c x.
60869      \param y Value of the pre-defined variable \c y.
60870      \param z Value of the pre-defined variable \c z.
60871      \param c Value of the pre-defined variable \c c.
60872      \return Result of the formula evaluation.
60873      \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
60874      \par Example
60875      \code
60876      const double
60877      res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2),  // will return '1'.
60878      res2 = cimg::eval(0,1,1);                    // will return '1' too.
60879      \endcode
60880   **/
60881   inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
60882     static const CImg<float> empty;
60883     return empty.eval(expression,x,y,z,c);
60884   }
60885 
60886   template<typename t>
60887   inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) {
60888     static const CImg<float> empty;
60889     return empty.eval(expression,xyzc);
60890   }
60891 
60892   // End of cimg:: namespace
60893 }
60894 
60895   // End of cimg_library:: namespace
60896 }
60897 
60898 //! Short alias name.
60899 namespace cil = cimg_library_suffixed;
60900 
60901 #ifdef _cimg_redefine_False
60902 #define False 0
60903 #endif
60904 #ifdef _cimg_redefine_True
60905 #define True 1
60906 #endif
60907 #ifdef _cimg_redefine_min
60908 #define min(a,b) (((a)<(b))?(a):(b))
60909 #endif
60910 #ifdef _cimg_redefine_max
60911 #define max(a,b) (((a)>(b))?(a):(b))
60912 #endif
60913 #ifdef _cimg_redefine_PI
60914 #define PI 3.141592653589793238462643383
60915 #endif
60916 #ifdef _MSC_VER
60917 #pragma warning(pop)
60918 #endif
60919 
60920 #endif
60921 // Local Variables:
60922 // mode: c++
60923 // End:
60924